2 * Copyright (c) 2014, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
12 (*****************************************************************************)
13 (* Module dealing with inheritance.
14 * When we want to declare a new class, we first have to retrieve all the
15 * types that were inherited from their parents.
17 (*****************************************************************************)
23 module Env
= Typing_env
24 module Inst
= Typing_instantiate
25 module TUtils
= Typing_utils
26 module Phase
= Typing_phase
30 (*****************************************************************************)
31 (* This is what we are trying to produce for a given class. *)
32 (*****************************************************************************)
35 ih_cstr
: class_elt
option * bool (* consistency required *);
36 ih_consts
: class_elt
SMap.t
;
37 ih_typeconsts
: typeconst_type
SMap.t
;
38 ih_props
: class_elt
SMap.t
;
39 ih_sprops
: class_elt
SMap.t
;
40 ih_methods
: class_elt
SMap.t
;
41 ih_smethods
: class_elt
SMap.t
;
45 ih_cstr
= None
, false;
46 ih_consts
= SMap.empty;
47 ih_typeconsts
= SMap.empty;
48 ih_props
= SMap.empty;
49 ih_sprops
= SMap.empty;
50 ih_methods
= SMap.empty;
51 ih_smethods
= SMap.empty;
54 (*****************************************************************************)
55 (* Functions used to merge an additional inherited class to the types
56 * we already inherited.
58 (*****************************************************************************)
60 let is_abstract_method x
=
62 | _
, Tfun x
when x
.ft_abstract
-> true
65 let add_method name sig_ methods
=
66 match (fst sig_
.ce_type
), sig_
.ce_synthesized
with
67 | Reason.Rdynamic_yield _
, true ->
68 (* DynamicYield::__call-derived pseudo-methods need to be
69 * rederived at each level *)
73 match SMap.get name methods
with
75 (* The method didn't exist so far, let's add it *)
76 SMap.add name sig_ methods
78 if ((not
(is_abstract_method old_sig
) && is_abstract_method sig_
)
79 || (is_abstract_method old_sig
= is_abstract_method sig_
80 && not
(old_sig
.ce_synthesized
) && sig_
.ce_synthesized
))
82 (* The then-branch of this if is encountered when the method being
83 * added shouldn't *actually* be added. When's that?
84 * In isolation, we can say that
85 * - We don't want to override a concrete method with
87 * - We don't want to override a method that's actually
88 * implemented by the programmer with one that's "synthetic",
89 * e.g. arising merely from a require-extends declaration in
91 * When these two considerations conflict, we give precedence to
92 * abstractness for determining priority of the method.
96 (* Otherwise, we *are* overwriting a method definition. This is
97 * OK when a naming conflict is parent class vs trait (trait
98 * wins!), but not really OK when the naming conflict is trait vs
99 * trait (we rely on HHVM to catch the error at runtime) *)
100 else SMap.add name
{sig_
with ce_override
= false} methods
103 let add_methods methods' acc
=
104 SMap.fold
add_method methods' acc
106 let add_members members acc
=
107 SMap.fold
SMap.add members acc
109 let is_abstract_typeconst x
= x
.ttc_type
= None
111 let can_override_typeconst x
=
112 (is_abstract_typeconst x
) || x
.ttc_constraint
<> None
114 let add_typeconst name sig_ typeconsts
=
115 match SMap.get name typeconsts
with
117 (* The type constant didn't exist so far, let's add it *)
118 SMap.add name sig_ typeconsts
119 (* This covers the following case
121 * interface I1 { abstract const type T; }
122 * interface I2 { const type T = int; }
124 * class C implements I1, I2 {}
126 * Then C::T == I2::T since I2::T is not abstract
129 when not
(is_abstract_typeconst old_sig
) && (is_abstract_typeconst sig_
) ->
131 (* This covers the following case
133 * abstract P { const type T as arraykey = arraykey; }
134 * interface I { const type T = int; }
136 * class C extends P implements I {}
138 * Then C::T == I::T since P::T has a constraint and thus can be overridden
139 * by it's child, while I::T cannot be overridden.
142 when not
(can_override_typeconst old_sig
) && (can_override_typeconst sig_
) ->
144 (* When a type constant is declared in multiple parents we need to make a
145 * subtle choice of what type we inherit. For example in:
147 * interface I1 { abstract const type t as Container<int>; }
148 * interface I2 { abstract const type t as KeyedContainer<int, int>; }
149 * abstract class C implements I1, I2 {}
151 * Depending on the order the interfaces are declared, we may report an error.
152 * Since this could be confusing there is special logic in Typing_extends that
153 * checks for this potentially ambiguous situation and warns the programmer to
154 * explicitly declare T in C.
157 SMap.add name sig_ typeconsts
159 let add_constructor (cstr
, cstr_consist
) (acc
, acc_consist
) =
160 let ce = match cstr
, acc
with
162 | Some
ce, Some acce
when ce.ce_synthesized
&& not acce
.ce_synthesized
->
165 in ce, cstr_consist
|| acc_consist
167 let add_inherited inherited acc
= {
168 ih_cstr
= add_constructor inherited
.ih_cstr acc
.ih_cstr
;
169 ih_consts
= add_members inherited
.ih_consts acc
.ih_consts
;
171 SMap.fold
add_typeconst inherited
.ih_typeconsts acc
.ih_typeconsts
;
172 ih_props
= add_members inherited
.ih_props acc
.ih_props
;
173 ih_sprops
= add_members inherited
.ih_sprops acc
.ih_sprops
;
174 ih_methods
= add_methods inherited
.ih_methods acc
.ih_methods
;
175 ih_smethods
= add_methods inherited
.ih_smethods acc
.ih_smethods
;
178 (*****************************************************************************)
180 (*****************************************************************************)
182 let desugar_class_hint = function
183 | (_
, Happly
((pos
, class_name
), type_parameters
)) ->
184 pos
, class_name
, type_parameters
187 let check_arity pos class_name class_type class_parameters
=
188 let arity = List.length class_type
.tc_tparams
in
189 if List.length class_parameters
<> arity
190 then Errors.class_arity pos class_type
.tc_pos class_name
arity;
193 let make_substitution pos class_name class_type class_parameters
=
194 check_arity pos class_name class_type class_parameters
;
195 Inst.make_subst class_type
.tc_tparams class_parameters
197 let constructor env subst
(cstr
, consistent
) = match cstr
with
198 | None
-> env
, (None
, consistent
)
200 let env, ty
= Inst.instantiate subst
env ce.ce_type
in
201 env, (Some
{ce with ce_type
= ty
}, consistent
)
203 let map_inherited f inh
=
205 ih_cstr
= (opt_map f
(fst inh
.ih_cstr
)), (snd inh
.ih_cstr
);
206 ih_typeconsts
= inh
.ih_typeconsts
;
207 ih_consts
= SMap.map f inh
.ih_consts
;
208 ih_props
= SMap.map f inh
.ih_props
;
209 ih_sprops
= SMap.map f inh
.ih_sprops
;
210 ih_methods
= SMap.map f inh
.ih_methods
;
211 ih_smethods
= SMap.map f inh
.ih_smethods
;
214 (*****************************************************************************)
215 (* Code filtering the private members (useful for inheritance) *)
216 (*****************************************************************************)
218 let filter_private x
=
219 SMap.fold
begin fun name class_elt acc
->
220 match class_elt
.ce_visibility
with
222 | Vpublic
| Vprotected _
-> SMap.add name class_elt acc
225 let chown_private owner
=
226 SMap.map
begin fun class_elt
->
227 match class_elt
.ce_visibility
with
228 | Vprivate _
-> {class_elt
with ce_visibility
= Vprivate owner
}
231 let apply_fn_to_class_elts fn class_type
= {
233 tc_consts
= fn class_type
.tc_consts
;
234 tc_typeconsts
= class_type
.tc_typeconsts
;
235 tc_props
= fn class_type
.tc_props
;
236 tc_sprops
= fn class_type
.tc_sprops
;
237 tc_methods
= fn class_type
.tc_methods
;
238 tc_smethods
= fn class_type
.tc_smethods
;
241 let filter_privates = apply_fn_to_class_elts filter_private
242 let chown_privates owner
= apply_fn_to_class_elts (chown_private owner
)
244 (*****************************************************************************)
245 (* Builds the inherited type when the class lives in Hack *)
246 (*****************************************************************************)
248 let inherit_hack_class c
env p class_name class_type argl
=
249 let subst = make_substitution p class_name class_type argl
in
250 let instantiate = SMap.map_env
(Inst.instantiate_ce
subst) in
252 match class_type.tc_kind
with
254 (* Change the private visibility to point to the inheriting class *)
255 chown_privates (snd c
.c_name
) class_type
256 | Ast.Cnormal
| Ast.Cabstract
| Ast.Cinterface
->
257 filter_privates class_type
258 | Ast.Cenum
-> class_type
260 let env, typeconsts
= SMap.map_env
(Inst.instantiate_typeconst
subst)
261 env class_type.tc_typeconsts
in
262 let env, consts
= instantiate env class_type.tc_consts
in
263 let env, props
= instantiate env class_type.tc_props
in
264 let env, sprops
= instantiate env class_type.tc_sprops
in
265 let env, methods
= instantiate env class_type.tc_methods
in
266 let env, smethods
= instantiate env class_type.tc_smethods
in
267 let cstr = Env.get_construct
env class_type in
268 let env, cstr = constructor env subst cstr in
272 ih_typeconsts
= typeconsts
;
275 ih_methods
= methods
;
276 ih_smethods
= smethods
;
280 (* mostly copy paste of inherit_hack_class *)
281 let inherit_hack_class_constants_only env p class_name
class_type argl
=
282 let subst = make_substitution p class_name
class_type argl
in
283 let instantiate = SMap.map_env
(Inst.instantiate_ce
subst) in
284 let env, consts
= instantiate env class_type.tc_consts
in
285 let env, typeconsts
= SMap.map_env
(Inst.instantiate_typeconst
subst)
286 env class_type.tc_typeconsts
in
287 let result = { empty with
289 ih_typeconsts
= typeconsts
;
293 (* This logic deals with importing XHP attributes from an XHP class
294 via the "attribute :foo;" syntax. *)
295 let inherit_hack_xhp_attrs_only env p class_name
class_type argl
=
296 let subst = make_substitution p class_name
class_type argl
in
297 (* Filter out properties that are not XHP attributes *)
299 SMap.fold
begin fun name class_elt acc
->
300 if class_elt
.ce_is_xhp_attr
then SMap.add name class_elt acc
else acc
301 end class_type.tc_props
SMap.empty in
302 let env, props = SMap.map_env
(Inst.instantiate_ce
subst) env props in
303 let result = { empty with ih_props
= props; } in
306 (*****************************************************************************)
308 let from_class c
env hint
=
309 let pos, class_name
, class_params
= desugar_class_hint hint
in
310 let env, class_params
= lfold
Typing_hint.hint
env class_params
in
311 let class_type = Env.get_class_dep
env class_name
in
312 match class_type with
314 (* The class lives in PHP, we don't know anything about it *)
317 (* The class lives in Hack *)
318 inherit_hack_class c
env pos class_name class_ class_params
320 (* mostly copy paste of from_class *)
321 let from_class_constants_only env hint
=
322 let pos, class_name
, class_params
= desugar_class_hint hint
in
323 let env, class_params
= lfold
Typing_hint.hint
env class_params
in
324 let class_type = Env.get_class_dep
env class_name
in
325 match class_type with
327 (* The class lives in PHP, we don't know anything about it *)
330 (* The class lives in Hack *)
331 inherit_hack_class_constants_only env pos class_name class_ class_params
333 let from_class_xhp_attrs_only env hint
=
334 let pos, class_name
, class_params
= desugar_class_hint hint
in
335 let env, class_params
= lfold
Typing_hint.hint
env class_params
in
336 let class_type = Env.get_class_dep
env class_name
in
337 match class_type with
339 (* The class lives in PHP, we don't know anything about it *)
342 (* The class lives in Hack *)
343 inherit_hack_xhp_attrs_only env pos class_name class_ class_params
345 let from_parent env c
=
347 (* In an abstract class or a trait, we assume the interfaces
348 * will be implemented in the future, so we take them as
349 * part of the class (as requested by dependency injection implementers)
352 | Ast.Cabstract
-> c
.c_implements
@ c
.c_extends
353 | Ast.Ctrait
-> c
.c_implements
@ c
.c_extends
@ c
.c_req_implements
356 let env, inherited_l
= lfold
(from_class c
) env extends in
357 env, List.fold_right
add_inherited inherited_l
empty
359 let from_requirements c
(env, acc
) reqs
=
360 let env, inherited
= from_class c
env reqs
in
361 let inherited = map_inherited
362 (fun ce -> { ce with ce_synthesized
= true })
364 env, add_inherited inherited acc
366 let from_trait c
(env, acc
) uses
=
367 let env, inherited = from_class c
env uses
in
368 env, add_inherited inherited acc
370 let from_xhp_attr_use (env, acc
) uses
=
371 let env, inherited = from_class_xhp_attrs_only env uses
in
372 env, add_inherited inherited acc
374 let from_interface_constants (env, acc
) impls
=
375 let env, inherited = from_class_constants_only env impls
in
376 env, add_inherited inherited acc
378 (*****************************************************************************)
379 (* The API to the outside *)
380 (*****************************************************************************)
383 (* members inherited from parent class ... *)
384 let acc = from_parent env c
in
385 let acc = List.fold_left
(from_requirements c
) acc c
.c_req_extends
in
386 (* ... are overridden with those inherited from used traits *)
387 let acc = List.fold_left
(from_trait c
) acc c
.c_uses
in
388 let acc = List.fold_left
from_xhp_attr_use acc c
.c_xhp_attr_uses
in
389 (* todo: what about the same constant defined in different interfaces
390 * we implement? We should forbid and say "constant already defined".
391 * to julien: where is the logic that check for duplicated things?
392 * todo: improve constant handling, see task #2487051
394 let acc = List.fold_left
from_interface_constants acc c
.c_req_implements
in
395 List.fold_left
from_interface_constants acc c
.c_implements