Make Typing_instantiate only work on decl ty
[hiphop-php.git] / hphp / hack / src / typing / typing_extends.ml
blob1491a0eff7677bb8e434d6fb41e2a86593ae936a
1 (**
2 * Copyright (c) 2014, Facebook, Inc.
3 * All rights reserved.
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.
9 *)
12 (*****************************************************************************)
13 (* Checks that a class implements an interface *)
14 (*****************************************************************************)
16 open Utils
17 open Typing_defs
18 open Typing_ops
20 module Env = Typing_env
21 module TUtils = Typing_utils
22 module Inst = Typing_instantiate
23 module Phase = Typing_phase
25 (*****************************************************************************)
26 (* Helpers *)
27 (*****************************************************************************)
29 let is_private = function
30 | { ce_visibility = Vprivate _; _ } -> true
31 | _ -> false
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
49 | Vprivate _ , _ ->
50 (* The only time this case should come into play is when the
51 * parent_class_elt comes from a trait *)
53 | Vpublic , Vpublic
54 | Vprotected _ , Vprotected _
55 | Vprotected _ , Vpublic -> ()
56 | _ ->
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 -> ()
68 | Vprivate _ ->
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
74 * trait *)
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
79 | _ -> ()
80 end parent_members
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
86 * a Tgeneric.
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)
99 | (_, _) ->
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 =
122 Errors.try_when
123 (f parent child)
124 ~when_: (fun () -> class_.tc_name <> origin &&
125 Errors.has_no_errors (f child parent))
126 ~do_: (fun error ->
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
137 if check_params then
138 if check_for_const
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)
145 ) in
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
157 then acc
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
166 | Some class_elt ->
167 check_override env parent_class class_ parent_class_elt class_elt
168 | None -> ()
169 end parent_members
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_ = [
182 class_.tc_props;
183 class_.tc_sprops;
184 class_.tc_methods;
185 class_.tc_smethods;
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;
198 ft_abstract = false;
199 ft_arity = Fstandard (0, 0);
200 ft_tparams = [];
201 ft_params = [];
202 ft_ret = r, Tprim Nast.Tvoid;
204 in { ce_final = false;
205 ce_is_xhp_attr = false;
206 ce_override = false;
207 ce_synthesized = true;
208 ce_visibility = Vpublic;
209 ce_type = r, Tfun ft;
210 ce_origin = name;
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
217 then (
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
234 | None, _ -> ()
235 ) else ()
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
244 let is_final =
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
250 * is abstract
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
255 let child_cstr =
256 if child_is_abstract
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
261 child_cstr
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 *)
272 let check x y =
273 if is_final
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
279 ~f:check
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
292 | Some tconst ->
293 check_ambiguous_inheritance tconst_check parent_tconst tconst
294 (fst tconst.ttc_name) class_ tconst.ttc_origin
295 | None ->
296 Errors.member_not_implemented
297 tconst_name parent_pos pos (fst parent_tconst.ttc_name)
298 end ptypeconsts
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
308 | None ->
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
312 end pconsts;
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
340 | _ -> assert false
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
352 Errors.try_
353 (fun () -> check_class_implements env parent_class class_)
354 (fun errorl ->
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)