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 (* Checks that a class implements an interface *)
14 (*****************************************************************************)
20 module Env
= Typing_env
21 module TUtils
= Typing_utils
22 module Inst
= Typing_instantiate
23 module Phase
= Typing_phase
25 (*****************************************************************************)
27 (*****************************************************************************)
29 let is_private = function
30 | { ce_visibility
= Vprivate _
; _
} -> true
33 (*****************************************************************************)
34 (* Given a map of members, check that the overriding is correct.
35 * Please note that 'members' has a very general meaning here.
36 * It can be class variables, methods, static methods etc ... The same logic
37 * is applied to verify that the overriding is correct.
39 (*****************************************************************************)
41 let use_parent_for_known = false
42 let check_partially_known_method_returns = true
43 let check_partially_known_method_params = false
44 let check_partially_known_method_visibility = true
46 (* Rules for visibility *)
47 let check_visibility parent_class_elt class_elt
=
48 match parent_class_elt
.ce_visibility
, class_elt
.ce_visibility
with
50 (* The only time this case should come into play is when the
51 * parent_class_elt comes from a trait *)
54 | Vprotected _
, Vprotected _
55 | Vprotected _
, Vpublic
-> ()
57 let parent_pos = Reason.to_pos
(fst parent_class_elt
.ce_type
) in
58 let pos = Reason.to_pos
(fst class_elt
.ce_type
) in
59 let parent_vis = TUtils.string_of_visibility parent_class_elt
.ce_visibility
in
60 let vis = TUtils.string_of_visibility class_elt
.ce_visibility
in
61 Errors.visibility_extends
vis pos parent_pos parent_vis
63 (* Check that all the required members are implemented *)
64 let check_members_implemented check_private parent_reason reason parent_members members
=
65 SMap.iter
begin fun member_name class_elt
->
66 match class_elt
.ce_visibility
with
67 | Vprivate _
when not check_private
-> ()
69 (* This case cannot be removed as long as we're forced to
70 * check against every extended parent by the fact that // decl
71 * parents aren't fully checked against grandparents; when
72 * (class) extends (class // decl) use (trait), the grandchild
73 * won't have access to private members of the grandparent
76 | _
when not
(SMap.mem member_name members
) ->
77 let defn_reason = Reason.to_pos
(fst class_elt
.ce_type
) in
78 Errors.member_not_implemented member_name parent_reason reason
defn_reason
82 (* When constant is overridden we need to check if the type is
83 * compatible with the previous type defined in the parent.
85 * Note that we determine if a constant is abstract by seeing if it is
88 let check_types_for_const env parent_type class_type
=
89 match (snd parent_type
, snd class_type
) with
90 | Tgeneric
(_
, None
), _
-> ()
91 (* parent abstract constant; no constraints *)
92 | Tgeneric
(_
, Some
(Ast.Constraint_as
, fty_parent
)),
93 Tgeneric
(_
, Some
(Ast.Constraint_as
, fty_child
)) ->
94 (* redeclaration of an abstract constant *)
95 ignore
(Phase.sub_type_decl env fty_parent fty_child
)
96 | Tgeneric
(_
, Some
(Ast.Constraint_as
, fty_parent
)), _
->
97 (* const definition constrained by parent abstract const *)
98 ignore
(Phase.sub_type_decl env fty_parent class_type
)
100 (* types should be the same *)
101 ignore
(Phase.unify_decl env parent_type class_type
)
102 (* An abstract member can be declared in multiple ancestors. Sometimes these
103 * declarations can be different, but yet compatible depending on which ancestor
104 * we inherit the member from. For example:
106 * interface I1 { abstract public function foo(): int; }
107 * interface I2 { abstract public function foo(): mixed; }
109 * abstract class C implements I1, I2 {}
111 * I1::foo() is compatible with I2::foo(), but not vice versa. Hack chooses the
112 * signature for C::foo() arbitrarily and can report an error if we make a
113 * "wrong" choice. We check for this case and emit an extra line in the error
114 * instructing the programmer to redeclare the member to remove the ambiguity.
116 * Note: We could detect this case and make the correct choice for the user, but
117 * this would require invalidating the current entry we have in the typing heap
118 * for this class. We cannot make this choice earlier during typing_decl because
119 * a class we depend on during the subtyping may not have been declared yet.
121 let check_ambiguous_inheritance f parent child
pos class_ origin
=
124 ~when_
: (fun () -> class_
.tc_name
<> origin
&&
125 Errors.has_no_errors
(f child parent
))
127 Errors.ambiguous_inheritance
pos class_
.tc_name origin error
)
129 (* Check that overriding is correct *)
130 let check_override env ?
(ignore_fun_return
= false) ?
(check_for_const
= false)
131 parent_class class_ parent_class_elt class_elt
=
132 let class_known = if use_parent_for_known then parent_class
.tc_members_fully_known
133 else class_
.tc_members_fully_known
in
134 let check_vis = class_known || check_partially_known_method_visibility in
135 if check_vis then check_visibility parent_class_elt class_elt
else ();
136 let check_params = class_known || check_partially_known_method_params in
139 then check_types_for_const env parent_class_elt
.ce_type class_elt
.ce_type
140 else match parent_class_elt
.ce_type
, class_elt
.ce_type
with
141 | (r_parent
, Tfun ft_parent
), (r_child
, Tfun ft_child
) ->
142 let subtype_funs = SubType.subtype_method ~check_return
:(
143 (not ignore_fun_return
) &&
144 (class_known || check_partially_known_method_returns)
146 let check (r1
, ft1
) (r2
, ft2
) () = ignore
(subtype_funs env r1 ft1 r2 ft2
) in
147 check_ambiguous_inheritance check (r_parent
, ft_parent
) (r_child
, ft_child
)
148 (Reason.to_pos r_child
) class_ class_elt
.ce_origin
149 | fty_parent
, fty_child
->
150 let pos = Reason.to_pos
(fst fty_child
) in
151 ignore
(unify_decl
pos Typing_reason.URnone env fty_parent fty_child
)
153 (* Privates are only visible in the parent, we don't need to check them *)
154 let filter_privates members
=
155 SMap.fold
begin fun name class_elt acc
->
156 if is_private class_elt
158 else SMap.add name class_elt acc
159 end members
SMap.empty
161 let check_members check_private env parent_class class_ parent_members members
=
162 let parent_members = if check_private
then parent_members
163 else filter_privates parent_members in
164 SMap.iter
begin fun member_name parent_class_elt
->
165 match SMap.get member_name members
with
167 check_override env parent_class class_ parent_class_elt class_elt
171 (*****************************************************************************)
172 (* Before checking that a class implements an interface, we have to
173 * substitute the type parameters with their real type.
175 (*****************************************************************************)
177 (* Instantiation basically applies the substitution *)
178 let instantiate_members subst env members
=
179 SMap.map_env
(Inst.instantiate_ce subst
) env members
181 let make_all_members class_
= [
188 (* The phantom class element that represents the default constructor:
189 * public function __construct() {}
191 * It isn't added to the tc_construct only because that's used to
192 * determine whether a child class needs to call parent::__construct *)
193 let default_constructor_ce class_
=
194 let pos, name
= class_
.tc_pos
, class_
.tc_name
in
195 let r = Reason.Rwitness
pos in (* reason doesn't get used in, e.g. arity checks *)
196 let ft = { ft_pos
= pos;
197 ft_deprecated
= None
;
199 ft_arity
= Fstandard
(0, 0);
202 ft_ret
= r, Tprim
Nast.Tvoid
;
204 in { ce_final
= false;
205 ce_is_xhp_attr
= false;
207 ce_synthesized
= true;
208 ce_visibility
= Vpublic
;
209 ce_type
= r, Tfun
ft;
213 (* When an interface defines a constructor, we check that they are compatible *)
214 let check_constructors env parent_class class_ psubst subst
=
215 let explicit_consistency = snd parent_class
.tc_construct
in
216 if parent_class
.tc_kind
= Ast.Cinterface
|| explicit_consistency
218 match (fst parent_class
.tc_construct
), (fst class_
.tc_construct
) with
219 | Some parent_cstr
, _
when parent_cstr
.ce_synthesized
-> ()
220 | Some parent_cstr
, None
->
221 let pos = fst parent_cstr
.ce_type
in
222 Errors.missing_constructor
(Reason.to_pos
pos)
223 | _
, Some cstr
when cstr
.ce_override
-> (* <<__UNSAFE_Construct>> *)
225 | Some parent_cstr
, Some cstr
->
226 let env, parent_cstr
= Inst.instantiate_ce psubst
env parent_cstr
in
227 let env, cstr
= Inst.instantiate_ce subst
env cstr
in
228 check_override env ~ignore_fun_return
:true parent_class class_ parent_cstr cstr
229 | None
, Some cstr
when explicit_consistency ->
230 let parent_cstr = default_constructor_ce parent_class
in
231 let env, parent_cstr = Inst.instantiate_ce psubst
env parent_cstr in
232 let env, cstr
= Inst.instantiate_ce subst
env cstr
in
233 check_override env ~ignore_fun_return
:true parent_class class_
parent_cstr cstr
237 (* Checks if a child is compatible with the type constant of its parent.
238 * This requires the child's constraint and assigned type to be a subtype of
239 * the parent's type constant.
241 let tconst_subsumption env parent_typeconst child_typeconst
=
242 let pos = fst child_typeconst
.ttc_name
in
243 let parent_pos = fst parent_typeconst
.ttc_name
in
245 Option.is_none parent_typeconst
.ttc_constraint
&&
246 Option.is_some parent_typeconst
.ttc_type
in
248 (* Check that the child's constraint is compatible with the parent. If the
249 * parent has a constraint then the child must also have a constraint if it
252 let child_is_abstract = Option.is_none child_typeconst
.ttc_type
in
253 let default = Reason.Rtconst_no_cstr child_typeconst
.ttc_name
,
254 Tgeneric
(snd child_typeconst
.ttc_name
, None
) in
257 then Some
(Option.value child_typeconst
.ttc_constraint ~
default)
258 else child_typeconst
.ttc_constraint
in
259 ignore
@@ Option.map2
260 parent_typeconst
.ttc_constraint
262 ~f
:(sub_type_decl
pos Reason.URsubsume_tconst_cstr
env);
264 (* Check that the child's assigned type satisifies parent constraint *)
265 ignore
@@ Option.map2
266 parent_typeconst
.ttc_constraint
267 child_typeconst
.ttc_type
268 ~f
:(sub_type_decl
parent_pos Reason.URtypeconst_cstr
env);
270 (* If the parent cannot be overridden, we unify the types otherwise we ensure
271 * the child's assigned type is compatible with the parent's *)
274 then ignore
(unify_decl
pos Reason.URsubsume_tconst_assign
env x y
)
275 else ignore
(sub_type_decl
pos Reason.URsubsume_tconst_assign
env x y
) in
276 ignore
@@ Option.map2
277 parent_typeconst
.ttc_type
278 child_typeconst
.ttc_type
281 (* For type constants we need to check that a child respects the
282 * constraints specified by its parent. *)
283 let check_typeconsts env parent_class class_
=
284 let parent_pos, parent_class
, _
= parent_class
in
285 let pos, class_
, _
= class_
in
286 let ptypeconsts = parent_class
.tc_typeconsts
in
287 let typeconsts = class_
.tc_typeconsts
in
288 let tconst_check parent_tconst tconst
() =
289 tconst_subsumption env parent_tconst tconst
in
290 SMap.iter
begin fun tconst_name parent_tconst
->
291 match SMap.get tconst_name
typeconsts with
293 check_ambiguous_inheritance tconst_check parent_tconst tconst
294 (fst tconst
.ttc_name
) class_ tconst
.ttc_origin
296 Errors.member_not_implemented
297 tconst_name
parent_pos pos (fst parent_tconst
.ttc_name
)
300 let check_consts env parent_class class_ psubst subst
=
301 let pconsts, consts
= parent_class
.tc_consts
, class_
.tc_consts
in
302 let env, pconsts = instantiate_members psubst
env pconsts in
303 let env, consts
= instantiate_members subst
env consts
in
304 let check_const_override = check_override env ~check_for_const
:true parent_class class_
in
305 SMap.iter
begin fun const_name parent_const
->
306 match SMap.get const_name consts
with
307 | Some const
-> check_const_override parent_const const
309 let parent_pos = Reason.to_pos
(fst parent_const
.ce_type
) in
310 Errors.member_not_implemented const_name
parent_pos
311 class_
.tc_pos parent_class
.tc_pos
315 let check_class_implements env parent_class class_
=
316 check_typeconsts env parent_class class_
;
317 let parent_pos, parent_class
, parent_tparaml
= parent_class
in
318 let pos, class_
, tparaml
= class_
in
319 let fully_known = class_
.tc_members_fully_known
in
320 let psubst = Inst.make_subst parent_class
.tc_tparams parent_tparaml
in
321 let subst = Inst.make_subst class_
.tc_tparams tparaml
in
322 check_consts env parent_class class_
psubst subst;
323 let pmemberl = make_all_members parent_class
in
324 let memberl = make_all_members class_
in
325 check_constructors env parent_class class_
psubst subst;
326 let env, pmemberl = lfold
(instantiate_members psubst) env pmemberl in
327 let env, memberl = lfold
(instantiate_members subst) env memberl in
328 let check_privates:bool = (parent_class
.tc_kind
= Ast.Ctrait
) in
329 if not
fully_known then () else
330 List.iter2
(check_members_implemented check_privates parent_pos pos) pmemberl memberl;
331 List.iter2
(check_members check_privates env parent_class class_
) pmemberl memberl;
334 (*****************************************************************************)
335 (* The externally visible function *)
336 (*****************************************************************************)
338 let open_class_hint = function
339 | r, Tapply
(name
, tparaml
) -> Reason.to_pos
r, name
, tparaml
342 let check_implements env parent_type type_
=
343 let parent_pos, parent_name
, parent_tparaml
= open_class_hint parent_type
in
344 let pos, name
, tparaml
= open_class_hint type_
in
345 let parent_class = Env.get_class
env (snd parent_name
) in
346 let class_ = Env.get_class
env (snd name
) in
347 match parent_class, class_ with
348 | None
, _
| _
, None
-> ()
349 | Some
parent_class, Some
class_ ->
350 let parent_class = parent_pos, parent_class, parent_tparaml
in
351 let class_ = pos, class_, tparaml
in
353 (fun () -> check_class_implements env parent_class class_)
355 let p_name_pos, p_name_str
= parent_name
in
356 let name_pos, name_str
= name
in
357 Errors.override
p_name_pos p_name_str
name_pos name_str errorl
)