Move Nast -> decl functions into Decl_nast and Decl_folded_class modules
[hiphop-php.git] / hphp / hack / src / decl / decl_folded_class.ml
blob01af549827830a2e560eab642d708a9102313b70
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 (*****************************************************************************)
11 (* Module used to declare class types.
12 * For each class we want to build a complete type, that is the type of
13 * the methods defined in the class plus everything that was inherited.
15 (*****************************************************************************)
16 open Hh_prelude
17 open Decl_defs
18 open Aast
19 open Shallow_decl_defs
20 open Typing_defs
21 open Typing_deps
22 module Reason = Typing_reason
23 module Inst = Decl_instantiate
24 module Attrs = Typing_defs.Attributes
25 module SN = Naming_special_names
27 (*****************************************************************************)
28 (* Checking that the kind of a class is compatible with its parent
29 * For example, a class cannot extend an interface, an interface cannot
30 * extend a trait etc ...
32 (*****************************************************************************)
34 let check_extend_kind
35 (parent_pos : Pos.t)
36 (parent_kind : Ast_defs.class_kind)
37 (parent_name : string)
38 (child_pos : Pos.t)
39 (child_kind : Ast_defs.class_kind)
40 (child_name : string) : unit =
41 match (parent_kind, child_kind) with
42 (* What is allowed *)
43 | ( (Ast_defs.Cabstract | Ast_defs.Cnormal),
44 (Ast_defs.Cabstract | Ast_defs.Cnormal) )
45 | (Ast_defs.Cabstract, Ast_defs.Cenum)
46 (* enums extend BuiltinEnum under the hood *)
47 | (Ast_defs.Ctrait, Ast_defs.Ctrait)
48 | (Ast_defs.Cinterface, Ast_defs.Cinterface) ->
50 | _ ->
51 (* What is disallowed *)
52 Errors.wrong_extend_kind
53 ~parent_pos
54 ~parent_kind
55 ~parent_name
56 ~child_pos
57 ~child_kind
58 ~child_name
60 (*****************************************************************************)
61 (* Functions used retrieve everything implemented in parent classes
62 * The return values:
63 * env: the new environment
64 * parents: the name of all the parents and grand parents of the class this
65 * includes traits.
66 * is_complete: true if all the parents live in Hack
68 (*****************************************************************************)
70 let disallow_trait_reuse (env : Decl_env.env) : bool =
71 TypecheckerOptions.disallow_trait_reuse (Decl_env.tcopt env)
73 let report_reused_trait
74 (parent_type : Decl_defs.decl_class_type)
75 (shallow_class : Shallow_decl_defs.shallow_class) : string -> unit =
76 Errors.trait_reuse
77 parent_type.dc_pos
78 parent_type.dc_name
79 shallow_class.sc_name
81 (**
82 * Verifies that a class never reuses the same trait throughout its hierarchy.
84 * Since Hack only has single inheritance and we already put up a warning for
85 * cyclic class hierarchies, if there is any overlap between our extends and
86 * our parents' extends, that overlap must be a trait.
88 * This does not hold for interfaces because they have multiple inheritance,
89 * but interfaces cannot use traits in the first place.
91 * XHP attribute dependencies don't actually pull the trait into the class,
92 * so we need to track them totally separately.
94 let check_no_duplicate_traits
95 (parent_type : Decl_defs.decl_class_type)
96 (shallow_class : Shallow_decl_defs.shallow_class)
97 (c_extends : SSet.t)
98 (full_extends : SSet.t) : unit =
99 let class_size = SSet.cardinal c_extends in
100 let parents_size = SSet.cardinal parent_type.dc_extends in
101 let full_size = SSet.cardinal full_extends in
102 if class_size + parents_size > full_size then
103 let duplicates = SSet.inter c_extends parent_type.dc_extends in
104 SSet.iter (report_reused_trait parent_type shallow_class) duplicates
107 * Adds the traits/classes which are part of a class' hierarchy.
109 * Traits are tracked separately but merged into the parents list when
110 * typechecking so that the class can access the trait members which are
111 * declared as private/protected.
113 let add_grand_parents_or_traits
114 (no_trait_reuse : bool)
115 (parent_pos : Pos.t)
116 (shallow_class : Shallow_decl_defs.shallow_class)
117 (acc : SSet.t * bool * [> `Extends_pass | `Xhp_pass ])
118 (parent_type : Decl_defs.decl_class_type) : SSet.t * bool * 'a =
119 let (extends, is_complete, pass) = acc in
120 let class_pos = fst shallow_class.sc_name in
121 let class_kind = shallow_class.sc_kind in
122 let class_name = snd shallow_class.sc_name in
123 if phys_equal pass `Extends_pass then
124 check_extend_kind
125 parent_pos
126 parent_type.dc_kind
127 parent_type.dc_name
128 class_pos
129 class_kind
130 class_name;
132 (* If we are crawling the xhp attribute deps, we need to merge their xhp deps
133 * as well *)
134 let parent_deps =
135 if phys_equal pass `Xhp_pass then
136 SSet.union parent_type.dc_extends parent_type.dc_xhp_attr_deps
137 else
138 parent_type.dc_extends
140 let extends' = SSet.union extends parent_deps in
141 (* Verify that merging the parent's extends did not introduce trait reuse *)
142 if no_trait_reuse then
143 check_no_duplicate_traits parent_type shallow_class extends extends';
144 (extends', parent_type.dc_members_fully_known && is_complete, pass)
146 let get_class_parent_or_trait
147 (env : Decl_env.env)
148 (shallow_class : Shallow_decl_defs.shallow_class)
149 ((parents, is_complete, pass) :
150 SSet.t * bool * [> `Extends_pass | `Xhp_pass ])
151 (ty : Typing_defs.decl_phase Typing_defs.ty) : SSet.t * bool * 'a =
152 (* See comment on check_no_duplicate_traits for reasoning here *)
153 let no_trait_reuse =
154 disallow_trait_reuse env
155 && (not (phys_equal pass `Xhp_pass))
156 && not Ast_defs.(equal_class_kind shallow_class.sc_kind Cinterface)
158 let (_, (parent_pos, parent), _) = Decl_utils.unwrap_class_type ty in
159 (* If we already had this exact trait, we need to flag trait reuse *)
160 let reused_trait = no_trait_reuse && SSet.mem parent parents in
161 let parents = SSet.add parent parents in
162 let parent_type = Decl_env.get_class_dep env parent in
163 match parent_type with
164 | None ->
165 (* The class lives in PHP *)
166 (parents, false, pass)
167 | Some parent_type ->
168 (* The parent class lives in Hack, so we can report reused traits *)
169 if reused_trait then report_reused_trait parent_type shallow_class parent;
170 let acc = (parents, is_complete, pass) in
171 add_grand_parents_or_traits
172 no_trait_reuse
173 parent_pos
174 shallow_class
176 parent_type
178 let get_class_parents_and_traits
179 (env : Decl_env.env) (shallow_class : Shallow_decl_defs.shallow_class) :
180 SSet.t * SSet.t * bool =
181 let parents = SSet.empty in
182 let is_complete = true in
183 (* extends parents *)
184 let acc = (parents, is_complete, `Extends_pass) in
185 let (parents, is_complete, _) =
186 List.fold_left
187 shallow_class.sc_extends
188 ~f:(get_class_parent_or_trait env shallow_class)
189 ~init:acc
191 (* traits *)
192 let acc = (parents, is_complete, `Traits_pass) in
193 let (parents, is_complete, _) =
194 List.fold_left
195 shallow_class.sc_uses
196 ~f:(get_class_parent_or_trait env shallow_class)
197 ~init:acc
199 (* XHP classes whose attributes were imported via "attribute :foo;" syntax *)
200 let acc = (SSet.empty, is_complete, `Xhp_pass) in
201 let (xhp_parents, is_complete, _) =
202 List.fold_left
203 shallow_class.sc_xhp_attr_uses
204 ~f:(get_class_parent_or_trait env shallow_class)
205 ~init:acc
207 (parents, xhp_parents, is_complete)
209 type class_env = {
210 ctx: Provider_context.t;
211 stack: SSet.t;
214 let check_if_cyclic (class_env : class_env) ((pos, cid) : Pos.t * string) : bool
216 let stack = class_env.stack in
217 let is_cyclic = SSet.mem cid stack in
218 if is_cyclic then Errors.cyclic_class_def stack pos;
219 is_cyclic
221 let shallow_decl_enabled (ctx : Provider_context.t) : bool =
222 TypecheckerOptions.shallow_class_decl (Provider_context.get_tcopt ctx)
224 let pu_enum_fold
225 origin
226 (acc : Typing_defs.pu_enum_type SMap.t)
227 (spu : Shallow_decl_defs.shallow_pu_enum) : Typing_defs.pu_enum_type SMap.t
229 let spu_name = snd spu.spu_name in
230 let tpu =
231 match SMap.find_opt spu_name acc with
232 | None -> Decl_to_typing.shallow_pu_enum_to_pu_enum_type origin spu
233 | Some tpu ->
234 let origin = { pu_class = origin; pu_enum = spu_name } in
236 tpu_name = spu.spu_name;
237 tpu_is_final = spu.spu_is_final;
238 tpu_case_types =
239 List.fold_left
240 spu.spu_case_types
241 ~init:tpu.tpu_case_types
242 ~f:(fun acc tp ->
243 let sid = snd tp.tp_name in
244 SMap.add sid (origin, tp) acc);
245 tpu_case_values =
246 List.fold_left
247 spu.spu_case_values
248 ~init:tpu.tpu_case_values
249 ~f:(fun acc (name, dty) ->
250 SMap.add (snd name) (origin, name, dty) acc);
251 tpu_members =
252 List.fold_left spu.spu_members ~init:tpu.tpu_members ~f:(fun acc sm ->
253 let tpum_types =
254 match SMap.find_opt (snd sm.spum_atom) acc with
255 | None -> SMap.empty
256 | Some tm -> tm.tpum_types
258 let tpum_exprs =
259 match SMap.find_opt (snd sm.spum_atom) acc with
260 | None -> SMap.empty
261 | Some tm -> tm.tpum_exprs
263 let tpum_types =
264 List.fold_left
265 sm.spum_types
266 ~init:tpum_types
267 ~f:(fun acc (sid, declty) ->
268 let k = snd sid in
269 SMap.add k (origin, sid, declty) acc)
271 let tpum_exprs =
272 List.fold_left sm.spum_exprs ~init:tpum_exprs ~f:(fun acc k ->
273 SMap.add (snd k) (origin, k) acc)
275 SMap.add
276 (snd sm.spum_atom)
278 tpum_atom = sm.spum_atom;
279 tpum_origin = origin;
280 tpum_types;
281 tpum_exprs;
283 acc);
286 SMap.add (snd spu.spu_name) tpu acc
288 let rec class_decl_if_missing
289 ~(sh : SharedMem.uses) (class_env : class_env) (c : Nast.class_) :
290 (string * Decl_defs.decl_class_type) option =
291 let ((_, cid) as c_name) = c.c_name in
292 if check_if_cyclic class_env c_name then
293 None
294 else if shallow_decl_enabled class_env.ctx then
295 (* This function is often called for its side effect of ensuring that the
296 class is declared. When shallow-decl is enabled, we still want this
297 side effect (for use cases like on-the-fly declaring entire files in
298 Decl_redecl_service for incremental typechecking), but since we are not
299 producing a folded class declaration, there is nothing we can return.
300 This is a code smell--we should use a function with a different
301 signature when we only want this side effect. *)
302 let (_ : shallow_class) =
303 Shallow_classes_provider.decl class_env.ctx ~use_cache:true c
305 None
306 else
307 match Decl_heap.Classes.get cid with
308 | Some class_ -> Some (cid, class_)
309 | None ->
310 (* Class elements are in memory if and only if the class itself is there.
311 * Exiting before class declaration is ready would break this invariant *)
312 WorkerCancel.with_no_cancellations @@ fun () ->
313 let class_ = class_naming_and_decl ~sh class_env cid c in
314 Some class_
316 and class_naming_and_decl
317 ~(sh : SharedMem.uses)
318 (class_env : class_env)
319 (cid : string)
320 (c : Nast.class_) : string * Decl_defs.decl_class_type =
321 let class_env = { class_env with stack = SSet.add cid class_env.stack } in
322 let shallow_class =
323 Shallow_classes_provider.decl class_env.ctx ~use_cache:false c
325 let (errors, tc) =
326 Errors.do_ (fun () ->
327 class_parents_decl ~sh class_env shallow_class;
328 class_decl ~sh class_env.ctx shallow_class)
330 let name = snd shallow_class.sc_name in
331 let class_ = { tc with dc_decl_errors = Some errors } in
332 Decl_heap.Classes.add name class_;
333 (name, class_)
335 and class_parents_decl
336 ~(sh : SharedMem.uses)
337 (class_env : class_env)
338 (c : Shallow_decl_defs.shallow_class) : unit =
339 let class_type class_ =
340 let (_ : Decl_defs.decl_class_type option) =
341 class_type_decl ~sh class_env class_
345 List.iter c.sc_extends class_type;
346 List.iter c.sc_implements class_type;
347 List.iter c.sc_uses class_type;
348 List.iter c.sc_xhp_attr_uses class_type;
349 List.iter c.sc_req_extends class_type;
350 List.iter c.sc_req_implements class_type;
351 let enum_includes =
352 Aast.enum_includes_map ~f:(fun et -> et.te_includes) c.sc_enum_type
354 List.iter enum_includes class_type;
357 and is_disposable_type (env : Decl_env.env) (hint : Typing_defs.decl_ty) : bool
359 match get_node hint with
360 | Tapply ((_, c), _) ->
361 begin
362 match Decl_env.get_class_dep env c with
363 | None -> false
364 | Some c -> c.dc_is_disposable
366 | _ -> false
368 and class_type_decl
369 ~(sh : SharedMem.uses) (class_env : class_env) (hint : Typing_defs.decl_ty)
370 : Decl_defs.decl_class_type option =
371 match get_node hint with
372 | Tapply ((_, cid), _) ->
373 begin
374 match Naming_provider.get_class_path class_env.ctx cid with
375 | Some fn when not (Decl_heap.Classes.mem cid) ->
376 (* We are supposed to redeclare the class *)
377 let class_opt = Ast_provider.find_class_in_file class_env.ctx fn cid in
378 Errors.run_in_context fn Errors.Decl (fun () ->
379 Option.Monad_infix.(
380 class_opt >>= class_decl_if_missing ~sh class_env >>| snd))
381 | _ -> None
383 | _ ->
384 (* This class lives in PHP land *)
385 None
387 and class_is_abstract (c : Shallow_decl_defs.shallow_class) : bool =
388 match c.sc_kind with
389 | Ast_defs.Cabstract
390 | Ast_defs.Cinterface
391 | Ast_defs.Ctrait
392 | Ast_defs.Cenum ->
393 true
394 | _ -> false
396 (* When all type constants have been inherited and declared, this step synthesizes
397 * the defaults of abstract type constants into concrete type constants. *)
398 and synthesize_defaults
399 (k : string)
400 (tc : Typing_defs.typeconst_type)
401 ((typeconsts, consts) :
402 Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t) :
403 Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t =
404 match tc.ttc_abstract with
405 | TCAbstract (Some default) ->
406 let concrete =
408 tc with
409 ttc_abstract = TCConcrete;
410 ttc_constraint = None;
411 ttc_type = Some default;
414 let typeconsts = SMap.add k concrete typeconsts in
415 (* OCaml 4.06 has an update method that makes this operation much more ergonomic *)
416 let constant = SMap.find_opt k consts in
417 let consts =
418 Option.value_map constant ~default:consts ~f:(fun c ->
419 SMap.add k { c with cc_abstract = false } consts)
421 (typeconsts, consts)
422 | _ -> (typeconsts, consts)
424 and class_decl
425 ~(sh : SharedMem.uses)
426 (ctx : Provider_context.t)
427 (c : Shallow_decl_defs.shallow_class) : Decl_defs.decl_class_type =
428 let is_abstract = class_is_abstract c in
429 let const = Attrs.mem SN.UserAttributes.uaConst c.sc_user_attributes in
430 let (_p, cls_name) = c.sc_name in
431 let class_dep = Dep.Class cls_name in
432 let env = { Decl_env.mode = c.sc_mode; droot = Some class_dep; ctx } in
433 let inherited = Decl_inherit.make env c in
434 let props = inherited.Decl_inherit.ih_props in
435 let props =
436 List.fold_left ~f:(prop_decl ~write_shmem:true c) ~init:props c.sc_props
438 let m = inherited.Decl_inherit.ih_methods in
439 let (m, condition_types) =
440 List.fold_left
441 ~f:(method_decl_acc ~write_shmem:true ~is_static:false c)
442 ~init:(m, SSet.empty)
443 c.sc_methods
445 let consts = inherited.Decl_inherit.ih_consts in
446 let consts =
447 List.fold_left ~f:(class_const_fold c) ~init:consts c.sc_consts
449 let consts = SMap.add SN.Members.mClass (class_class_decl c.sc_name) consts in
450 let typeconsts = inherited.Decl_inherit.ih_typeconsts in
451 let (typeconsts, consts) =
452 List.fold_left
453 c.sc_typeconsts
454 ~f:(typeconst_fold c)
455 ~init:(typeconsts, consts)
457 let (typeconsts, consts) =
458 if Ast_defs.(equal_class_kind c.sc_kind Cnormal) then
459 SMap.fold synthesize_defaults typeconsts (typeconsts, consts)
460 else
461 (typeconsts, consts)
463 let pu_enums = inherited.Decl_inherit.ih_pu_enums in
464 let pu_enums =
465 List.fold_left c.sc_pu_enums ~f:(pu_enum_fold cls_name) ~init:pu_enums
467 let sclass_var = static_prop_decl ~write_shmem:true c in
468 let sprops = inherited.Decl_inherit.ih_sprops in
469 let sprops = List.fold_left c.sc_sprops ~f:sclass_var ~init:sprops in
470 let sm = inherited.Decl_inherit.ih_smethods in
471 let (sm, condition_types) =
472 List.fold_left
473 c.sc_static_methods
474 ~f:(method_decl_acc ~write_shmem:true ~is_static:true c)
475 ~init:(sm, condition_types)
477 let parent_cstr = inherited.Decl_inherit.ih_cstr in
478 let cstr = constructor_decl ~sh parent_cstr c in
479 let has_concrete_cstr =
480 match fst cstr with
481 | None -> false
482 | Some elt when get_elt_abstract elt -> false
483 | _ -> true
485 let impl = c.sc_extends @ c.sc_implements @ c.sc_uses in
486 let impl =
487 match
488 List.find c.sc_methods ~f:(fun sm ->
489 String.equal (snd sm.sm_name) SN.Members.__toString)
490 with
491 | Some { sm_name = (pos, _); _ }
492 when String.( <> ) cls_name SN.Classes.cStringish ->
493 (* HHVM implicitly adds Stringish interface for every class/iface/trait
494 * with a __toString method; "string" also implements this interface *)
495 (* Declare Stringish and parents if not already declared *)
496 let class_env = { ctx; stack = SSet.empty } in
497 let ty =
498 mk (Reason.Rhint pos, Tapply ((pos, SN.Classes.cStringish), []))
500 let (_ : Decl_defs.decl_class_type option) =
501 class_type_decl ~sh class_env ty
503 ty :: impl
504 | _ -> impl
506 let impl = List.map impl (get_implements env) in
507 let impl = List.fold_right impl ~f:(SMap.fold SMap.add) ~init:SMap.empty in
508 let (extends, xhp_attr_deps, ext_strict) =
509 get_class_parents_and_traits env c
511 let (req_ancestors, req_ancestors_extends) =
512 Decl_requirements.get_class_requirements env c
514 (* Interfaces IDisposable and IAsyncDisposable are *disposable types*, as
515 * are any classes that implement either of these interfaces, directly or
516 * indirectly. Also treat any trait that *requires* extension or
517 * implementation of a disposable class as disposable itself.
519 let is_disposable_class_name cls_name =
520 String.equal cls_name SN.Classes.cIDisposable
521 || String.equal cls_name SN.Classes.cIAsyncDisposable
523 let is_disposable =
524 is_disposable_class_name cls_name
525 || SMap.exists (fun n _ -> is_disposable_class_name n) impl
526 || List.exists
527 (c.sc_req_extends @ c.sc_req_implements)
528 (is_disposable_type env)
530 (* If this class is disposable then we require that any extended class or
531 * trait that is used, is also disposable, in order that escape analysis
532 * has been applied on the $this parameter.
534 let ext_strict =
535 List.fold_left c.sc_uses ~f:(trait_exists env) ~init:ext_strict
537 let enum = c.sc_enum_type in
538 let enum_inner_ty = SMap.find_opt SN.FB.tInner typeconsts in
539 let consts =
540 Decl_enum.rewrite_class
541 c.sc_name
542 enum
543 Option.(enum_inner_ty >>= fun t -> t.ttc_type)
544 (fun x -> SMap.find_opt x impl)
545 consts
547 let has_own_cstr = has_concrete_cstr && Option.is_some c.sc_constructor in
548 let deferred_members =
549 if shallow_decl_enabled ctx then
550 SSet.empty
551 else
552 snd (Decl_init_check.class_ ~has_own_cstr env c)
554 let sealed_whitelist = get_sealed_whitelist c in
555 let tc =
557 dc_final = c.sc_final;
558 dc_const = const;
559 dc_abstract = is_abstract;
560 dc_need_init = has_concrete_cstr;
561 dc_deferred_init_members = deferred_members;
562 dc_members_fully_known = ext_strict;
563 dc_kind = c.sc_kind;
564 dc_is_xhp = c.sc_is_xhp;
565 dc_has_xhp_keyword = c.sc_has_xhp_keyword;
566 dc_is_disposable = is_disposable;
567 dc_name = snd c.sc_name;
568 dc_pos = fst c.sc_name;
569 dc_tparams = c.sc_tparams;
570 dc_where_constraints = c.sc_where_constraints;
571 dc_substs = inherited.Decl_inherit.ih_substs;
572 dc_consts = consts;
573 dc_typeconsts = typeconsts;
574 dc_pu_enums = pu_enums;
575 dc_props = props;
576 dc_sprops = sprops;
577 dc_methods = m;
578 dc_smethods = sm;
579 dc_construct = cstr;
580 dc_ancestors = impl;
581 dc_extends = extends;
582 dc_sealed_whitelist = sealed_whitelist;
583 dc_xhp_attr_deps = xhp_attr_deps;
584 dc_req_ancestors = req_ancestors;
585 dc_req_ancestors_extends = req_ancestors_extends;
586 dc_enum_type = enum;
587 dc_decl_errors = None;
588 dc_condition_types = condition_types;
591 SMap.iter
592 begin
593 fun x _ ->
594 Typing_deps.add_idep class_dep (Dep.Class x)
596 impl;
599 and get_sealed_whitelist (c : Shallow_decl_defs.shallow_class) : SSet.t option =
600 match Attributes.find SN.UserAttributes.uaSealed c.sc_user_attributes with
601 | None -> None
602 | Some { ua_classname_params; _ } -> Some (SSet.of_list ua_classname_params)
604 and get_implements (env : Decl_env.env) (ht : Typing_defs.decl_ty) :
605 Typing_defs.decl_ty SMap.t =
606 let (_r, (_p, c), paraml) = Decl_utils.unwrap_class_type ht in
607 let class_ = Decl_env.get_class_dep env c in
608 match class_ with
609 | None ->
610 (* The class lives in PHP land *)
611 SMap.singleton c ht
612 | Some class_ ->
613 let subst = Inst.make_subst class_.dc_tparams paraml in
614 let sub_implements =
615 SMap.map (fun ty -> Inst.instantiate subst ty) class_.dc_ancestors
617 SMap.add c ht sub_implements
619 and trait_exists (env : Decl_env.env) (acc : bool) (trait : Typing_defs.decl_ty)
620 : bool =
621 match get_node trait with
622 | Tapply ((_, trait), _) ->
623 let class_ = Decl_env.get_class_dep env trait in
624 (match class_ with
625 | None -> false
626 | Some _class -> acc)
627 | _ -> false
629 and constructor_decl
630 ~(sh : SharedMem.uses)
631 ((pcstr, pconsist) : Decl_defs.element option * Typing_defs.consistent_kind)
632 (class_ : Shallow_decl_defs.shallow_class) :
633 Decl_defs.element option * Typing_defs.consistent_kind =
634 let SharedMem.Uses = sh in
635 (* constructors in children of class_ must be consistent? *)
636 let cconsist =
637 if class_.sc_final then
638 FinalClass
639 else if
640 Attrs.mem
641 SN.UserAttributes.uaConsistentConstruct
642 class_.sc_user_attributes
643 then
644 ConsistentConstruct
645 else
646 Inconsistent
648 let cstr =
649 match class_.sc_constructor with
650 | None -> pcstr
651 | Some method_ -> build_constructor ~write_shmem:true class_ method_
653 (cstr, Decl_utils.coalesce_consistent pconsist cconsist)
655 and build_constructor
656 ~(write_shmem : bool)
657 (class_ : Shallow_decl_defs.shallow_class)
658 (method_ : Shallow_decl_defs.shallow_method) : Decl_defs.element option =
659 let (_, class_name) = class_.sc_name in
660 let vis = visibility class_name method_.sm_visibility in
661 let pos = fst method_.sm_name in
662 let cstr =
664 elt_flags =
665 make_ce_flags
666 ~xhp_attr:None
667 ~final:method_.sm_final
668 ~abstract:method_.sm_abstract
669 ~lateinit:false
670 ~const:false
671 ~lsb:false
672 ~memoizelsb:false
673 ~synthesized:false
674 ~override:false
675 ~dynamicallycallable:false;
676 elt_visibility = vis;
677 elt_origin = class_name;
678 elt_reactivity = None;
679 elt_deprecated = method_.sm_deprecated;
682 let fe =
684 fe_pos = pos;
685 fe_deprecated = method_.sm_deprecated;
686 fe_type = method_.sm_type;
687 fe_php_std_lib = false;
690 if write_shmem then Decl_heap.Constructors.add class_name fe;
691 Some cstr
693 and class_const_fold
694 (c : Shallow_decl_defs.shallow_class)
695 (acc : Typing_defs.class_const SMap.t)
696 (scc : Shallow_decl_defs.shallow_class_const) :
697 Typing_defs.class_const SMap.t =
698 let c_name = snd c.sc_name in
699 let cc =
701 cc_synthesized = false;
702 cc_abstract = scc.scc_abstract;
703 cc_pos = fst scc.scc_name;
704 cc_type = scc.scc_type;
705 cc_origin = c_name;
708 let acc = SMap.add (snd scc.scc_name) cc acc in
711 (* Every class, interface, and trait implicitly defines a ::class to
712 * allow accessing its fully qualified name as a string *)
713 and class_class_decl (class_id : Ast_defs.id) : Typing_defs.class_const =
714 let (pos, name) = class_id in
715 let reason = Reason.Rclass_class (pos, name) in
716 let classname_ty =
717 mk (reason, Tapply ((pos, SN.Classes.cClassname), [mk (reason, Tthis)]))
720 cc_abstract = false;
721 cc_pos = pos;
722 cc_synthesized = true;
723 cc_type = classname_ty;
724 cc_origin = name;
727 and prop_decl
728 ~(write_shmem : bool)
729 (c : Shallow_decl_defs.shallow_class)
730 (acc : Decl_defs.element SMap.t)
731 (sp : Shallow_decl_defs.shallow_prop) : Decl_defs.element SMap.t =
732 let (sp_pos, sp_name) = sp.sp_name in
733 let ty =
734 match sp.sp_type with
735 | None -> mk (Reason.Rwitness sp_pos, Typing_defs.make_tany ())
736 | Some ty' -> ty'
738 let vis = visibility (snd c.sc_name) sp.sp_visibility in
739 let elt =
741 elt_flags =
742 make_ce_flags
743 ~xhp_attr:sp.sp_xhp_attr
744 ~final:true
745 ~lsb:false
746 ~synthesized:false
747 ~override:false
748 ~memoizelsb:false
749 ~const:sp.sp_const
750 ~lateinit:sp.sp_lateinit
751 ~abstract:sp.sp_abstract
752 ~dynamicallycallable:false;
753 elt_visibility = vis;
754 elt_origin = snd c.sc_name;
755 elt_reactivity = None;
756 elt_deprecated = None;
759 if write_shmem then Decl_heap.Props.add (elt.elt_origin, sp_name) ty;
760 let acc = SMap.add sp_name elt acc in
763 and static_prop_decl
764 ~(write_shmem : bool)
765 (c : Shallow_decl_defs.shallow_class)
766 (acc : Decl_defs.element SMap.t)
767 (sp : Shallow_decl_defs.shallow_prop) : Decl_defs.element SMap.t =
768 let (sp_pos, sp_name) = sp.sp_name in
769 let ty =
770 match sp.sp_type with
771 | None -> mk (Reason.Rwitness sp_pos, Typing_defs.make_tany ())
772 | Some ty' -> ty'
774 let vis = visibility (snd c.sc_name) sp.sp_visibility in
775 let elt =
777 elt_flags =
778 make_ce_flags
779 ~xhp_attr:sp.sp_xhp_attr
780 ~final:true
781 ~const:sp.sp_const
782 ~lateinit:sp.sp_lateinit
783 ~lsb:sp.sp_lsb
784 ~override:false
785 ~memoizelsb:false
786 ~abstract:sp.sp_abstract
787 ~synthesized:false
788 ~dynamicallycallable:false;
789 elt_visibility = vis;
790 elt_origin = snd c.sc_name;
791 elt_reactivity = None;
792 elt_deprecated = None;
795 if write_shmem then Decl_heap.StaticProps.add (elt.elt_origin, sp_name) ty;
796 let acc = SMap.add sp_name elt acc in
799 and visibility (cid : string) (visibility : Aast_defs.visibility) :
800 Typing_defs.visibility =
801 match visibility with
802 | Public -> Vpublic
803 | Protected -> Vprotected cid
804 | Private -> Vprivate cid
806 (* each concrete type constant T = <sometype> implicitly defines a
807 class constant with the same name which is TypeStructure<sometype> *)
808 and typeconst_structure
809 (c : Shallow_decl_defs.shallow_class)
810 (stc : Shallow_decl_defs.shallow_typeconst) : Typing_defs.class_const =
811 let pos = fst stc.stc_name in
812 let r = Reason.Rwitness pos in
813 let tsid = (pos, SN.FB.cTypeStructure) in
814 let ts_ty =
815 mk (r, Tapply (tsid, [mk (r, Taccess (mk (r, Tthis), [stc.stc_name]))]))
817 let abstract =
818 match stc.stc_abstract with
819 | TCAbstract _ -> true
820 | _ -> false
823 cc_abstract = abstract;
824 cc_pos = pos;
825 cc_synthesized = true;
826 cc_type = ts_ty;
827 cc_origin = snd c.sc_name;
830 and typeconst_fold
831 (c : Shallow_decl_defs.shallow_class)
832 (acc : Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t)
833 (stc : Shallow_decl_defs.shallow_typeconst) :
834 Typing_defs.typeconst_type SMap.t * Typing_defs.class_const SMap.t =
835 let (typeconsts, consts) = acc in
836 match c.sc_kind with
837 | Ast_defs.Ctrait
838 | Ast_defs.Cenum ->
840 | Ast_defs.Cinterface
841 | Ast_defs.Cabstract
842 | Ast_defs.Cnormal ->
843 let name = snd stc.stc_name in
844 let c_name = snd c.sc_name in
845 let ts = typeconst_structure c stc in
846 let consts = SMap.add name ts consts in
847 let ptc_opt = SMap.find_opt name typeconsts in
848 let enforceable =
849 (* Without the positions, this is a simple OR, but this way allows us to
850 * report the position of the <<__Enforceable>> attribute to the user *)
851 if snd stc.stc_enforceable then
852 stc.stc_enforceable
853 else
854 match ptc_opt with
855 | Some ptc -> ptc.ttc_enforceable
856 | None -> (Pos.none, false)
858 let reifiable =
859 if Option.is_some stc.stc_reifiable then
860 stc.stc_reifiable
861 else
862 Option.bind ptc_opt (fun ptc -> ptc.ttc_reifiable)
864 let tc =
866 ttc_abstract = stc.stc_abstract;
867 ttc_name = stc.stc_name;
868 ttc_constraint = stc.stc_constraint;
869 ttc_type = stc.stc_type;
870 ttc_origin = c_name;
871 ttc_enforceable = enforceable;
872 ttc_reifiable = reifiable;
875 let typeconsts = SMap.add (snd stc.stc_name) tc typeconsts in
876 (typeconsts, consts)
878 and method_decl_acc
879 ~(write_shmem : bool)
880 ~(is_static : bool)
881 (c : Shallow_decl_defs.shallow_class)
882 ((acc, condition_types) : Decl_defs.element SMap.t * SSet.t)
883 (m : Shallow_decl_defs.shallow_method) : Decl_defs.element SMap.t * SSet.t =
884 (* If method doesn't override anything but has the <<__Override>> attribute, then
885 * set the override flag in ce_flags and let typing emit an appropriate error *)
886 let check_override = m.sm_override && not (SMap.mem (snd m.sm_name) acc) in
887 let (pos, id) = m.sm_name in
888 let get_reactivity t =
889 match get_node t with
890 | Tfun { ft_reactive; _ } -> ft_reactive
891 | _ -> Local None
893 let condition_types =
894 match get_reactivity m.sm_type with
895 | Pure (Some ty)
896 | Reactive (Some ty)
897 | Shallow (Some ty)
898 | Local (Some ty) ->
899 begin
900 match get_node ty with
901 | Tapply ((_, cls), []) -> SSet.add cls condition_types
902 | _ -> condition_types
904 | _ -> condition_types
906 let vis =
907 match (SMap.find_opt id acc, m.sm_visibility) with
908 | (Some { elt_visibility = Vprotected _ as parent_vis; _ }, Protected) ->
909 parent_vis
910 | _ -> visibility (snd c.sc_name) m.sm_visibility
912 let elt =
914 elt_flags =
915 make_ce_flags
916 ~xhp_attr:None
917 ~final:m.sm_final
918 ~abstract:m.sm_abstract
919 ~override:check_override
920 ~synthesized:false
921 ~lsb:false
922 ~memoizelsb:false
923 ~const:false
924 ~lateinit:false
925 ~dynamicallycallable:m.sm_dynamicallycallable;
926 elt_visibility = vis;
927 elt_origin = snd c.sc_name;
928 elt_reactivity = m.sm_reactivity;
929 elt_deprecated = m.sm_deprecated;
932 let fe =
934 fe_pos = pos;
935 fe_deprecated = None;
936 fe_type = m.sm_type;
937 fe_php_std_lib = false;
940 if write_shmem then
941 if is_static then
942 Decl_heap.StaticMethods.add (elt.elt_origin, id) fe
943 else
944 Decl_heap.Methods.add (elt.elt_origin, id) fe;
945 let acc = SMap.add id elt acc in
946 (acc, condition_types)