enable class constants in folded classes
[hiphop-php.git] / hphp / hack / src / rupro / lib / folded_decl_provider / provider.rs
blobedd44f1e5e42ccd8050c29927f7b06b77a61342d
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use crate::alloc::Allocator;
7 use crate::decl_defs::{
8     CeVisibility, ClassConst, ClassConstKind, ClassEltFlags, ClassEltFlagsArgs, ConsistentKind,
9     DeclTy, DeclTy_, FoldedClass, FoldedElement, ShallowClass, ShallowMethod, ShallowProp,
10     UserAttribute, Visibility,
12 use crate::folded_decl_provider::FoldedDeclCache;
13 use crate::folded_decl_provider::{inherit::Inherited, subst::Subst};
14 use crate::reason::{Reason, ReasonImpl};
15 use crate::shallow_decl_provider::ShallowDeclProvider;
16 use crate::special_names::SpecialNames;
17 use pos::{
18     ClassConstName, MethodNameMap, ModuleName, Positioned, PropNameMap, TypeName, TypeNameMap,
19     TypeNameSet,
21 use std::sync::Arc;
23 // note(sf, 2022-02-03): c.f. hphp/hack/src/decl/decl_folded_class.ml
25 #[derive(Debug)]
26 pub struct FoldedDeclProvider<R: Reason> {
27     cache: Arc<dyn FoldedDeclCache<Reason = R>>,
28     alloc: &'static Allocator<R>,
29     special_names: &'static SpecialNames,
30     shallow_decl_provider: Arc<ShallowDeclProvider<R>>,
33 impl<R: Reason> FoldedDeclProvider<R> {
34     pub fn new(
35         cache: Arc<dyn FoldedDeclCache<Reason = R>>,
36         alloc: &'static Allocator<R>,
37         special_names: &'static SpecialNames,
38         shallow_decl_provider: Arc<ShallowDeclProvider<R>>,
39     ) -> Self {
40         Self {
41             cache,
42             alloc,
43             special_names,
44             shallow_decl_provider,
45         }
46     }
48     pub fn get_folded_class(&self, name: TypeName) -> Option<Arc<FoldedClass<R>>> {
49         let mut stack = Default::default();
50         self.get_folded_class_impl(&mut stack, name)
51     }
53     fn detect_cycle(&self, stack: &mut TypeNameSet, pos_id: &Positioned<TypeName, R::Pos>) -> bool {
54         if stack.contains(&pos_id.id()) {
55             todo!("TODO(hrust): register error");
56         }
57         false
58     }
60     fn visibility(
61         &self,
62         cls: TypeName,
63         module: Option<&Positioned<ModuleName, R::Pos>>,
64         vis: Visibility,
65     ) -> CeVisibility<R::Pos> {
66         match vis {
67             Visibility::Public => CeVisibility::Public,
68             Visibility::Private => CeVisibility::Private(cls),
69             Visibility::Protected => CeVisibility::Protected(cls),
70             Visibility::Internal => module.map_or(CeVisibility::Public, |pid| {
71                 CeVisibility::Internal(pid.clone())
72             }),
73         }
74     }
76     /// Every class, interface, and trait implicitly defines a `::class` to allow
77     /// accessing its fully qualified name as a string.
78     fn decl_class_class(&self, class_id: &Positioned<TypeName, R::Pos>) -> ClassConst<R> {
79         // note(sf, 2022-02-08): c.f. Decl_folded_class.class_class_decl
80         let pos = class_id.pos();
81         let name = class_id.id();
82         let reason = R::mk(|| ReasonImpl::RclassClass(pos.clone(), name));
83         let classname_ty = self.alloc.decl_ty(
84             reason.clone(),
85             DeclTy_::DTapply(
86                 Positioned::new(pos.clone(), self.special_names.classes.cClassname),
87                 vec![self.alloc.decl_ty(reason, DeclTy_::DTthis)],
88             ),
89         );
90         ClassConst {
91             is_synthesized: true,
92             kind: ClassConstKind::CCConcrete,
93             pos: pos.clone(),
94             ty: classname_ty,
95             origin: name.clone(),
96             refs: Vec::new(),
97         }
98     }
100     fn decl_prop(
101         &self,
102         props: &mut PropNameMap<FoldedElement<R>>,
103         sc: &ShallowClass<R>,
104         sp: &ShallowProp<R>,
105     ) {
106         // note(sf, 2022-02-08): c.f. Decl_folded_class.prop_decl
107         let cls = sc.name.id();
108         let prop = sp.name.id();
109         let vis = self.visibility(cls, sc.module.as_ref(), sp.visibility);
110         let prop_flags = &sp.flags;
111         let flag_args = ClassEltFlagsArgs {
112             xhp_attr: sp.xhp_attr,
113             is_abstract: prop_flags.is_abstract(),
114             is_final: true,
115             is_superfluous_override: false,
116             is_lsb: false,
117             is_synthesized: false,
118             is_const: prop_flags.is_const(),
119             is_lateinit: prop_flags.is_lateinit(),
120             is_dynamicallycallable: false,
121             is_readonly_prop: prop_flags.is_readonly(),
122             supports_dynamic_type: false,
123             needs_init: prop_flags.needs_init(),
124         };
125         let elt = FoldedElement {
126             origin: sc.name.id(),
127             visibility: vis,
128             deprecated: None,
129             flags: ClassEltFlags::new(flag_args),
130         };
131         props.insert(prop, elt);
132     }
134     fn decl_static_prop(
135         &self,
136         static_props: &mut PropNameMap<FoldedElement<R>>,
137         sc: &ShallowClass<R>,
138         sp: &ShallowProp<R>,
139     ) {
140         let cls = sc.name.id();
141         let prop = sp.name.id();
142         let vis = self.visibility(cls, sc.module.as_ref(), sp.visibility);
143         let prop_flags = &sp.flags;
144         let flag_args = ClassEltFlagsArgs {
145             xhp_attr: sp.xhp_attr,
146             is_abstract: prop_flags.is_abstract(),
147             is_final: true,
148             is_superfluous_override: false,
149             is_lsb: prop_flags.is_lsb(),
150             is_synthesized: false,
151             is_const: prop_flags.is_const(),
152             is_lateinit: prop_flags.is_lateinit(),
153             is_dynamicallycallable: false,
154             is_readonly_prop: prop_flags.is_readonly(),
155             supports_dynamic_type: false,
156             needs_init: prop_flags.needs_init(),
157         };
158         let elt = FoldedElement {
159             origin: sc.name.id(),
160             visibility: vis,
161             deprecated: None,
162             flags: ClassEltFlags::new(flag_args),
163         };
164         static_props.insert(prop, elt);
165     }
167     fn decl_method(
168         &self,
169         methods: &mut MethodNameMap<FoldedElement<R>>,
170         sc: &ShallowClass<R>,
171         sm: &ShallowMethod<R>,
172     ) {
173         let cls = sc.name.id();
174         let meth = sm.name.id();
175         let vis = match (methods.get(&meth), sm.visibility) {
176             (
177                 Some(FoldedElement {
178                     visibility: CeVisibility::Protected(cls),
179                     ..
180                 }),
181                 Visibility::Protected,
182             ) => CeVisibility::Protected(*cls),
183             (_, v) => self.visibility(cls, sc.module.as_ref(), v),
184         };
186         let meth_flags = &sm.flags;
187         let flag_args = ClassEltFlagsArgs {
188             xhp_attr: None,
189             is_abstract: meth_flags.is_abstract(),
190             is_final: meth_flags.is_final(),
191             is_superfluous_override: meth_flags.is_override() && !methods.contains_key(&meth),
192             is_lsb: false,
193             is_synthesized: false,
194             is_const: false,
195             is_lateinit: false,
196             is_dynamicallycallable: meth_flags.is_dynamicallycallable(),
197             is_readonly_prop: false,
198             supports_dynamic_type: meth_flags.supports_dynamic_type(),
199             needs_init: false,
200         };
201         let elt = FoldedElement {
202             origin: cls,
203             visibility: vis,
204             deprecated: sm.deprecated,
205             flags: ClassEltFlags::new(flag_args),
206         };
208         methods.insert(meth, elt);
209     }
211     fn decl_class_type(
212         &self,
213         stack: &mut TypeNameSet,
214         acc: &mut TypeNameMap<Arc<FoldedClass<R>>>,
215         ty: &DeclTy<R>,
216     ) {
217         match &**ty.node() {
218             DeclTy_::DTapply(pos_id, _tyl) => {
219                 if !self.detect_cycle(stack, pos_id) {
220                     if let Some(folded_decl) = self.get_folded_class_impl(stack, pos_id.id()) {
221                         acc.insert(pos_id.id(), folded_decl);
222                     }
223                 }
224             }
225             _ => {}
226         }
227     }
229     fn decl_class_parents(
230         &self,
231         stack: &mut TypeNameSet,
232         sc: &ShallowClass<R>,
233     ) -> TypeNameMap<Arc<FoldedClass<R>>> {
234         let mut acc = Default::default();
235         sc.extends
236             .iter()
237             .for_each(|ty| self.decl_class_type(stack, &mut acc, ty));
238         acc
239     }
241     fn decl_constructor(&self, constructor: &mut Option<FoldedElement<R>>, sc: &ShallowClass<R>) {
242         // Constructors in children of `sc` must be consistent?
243         let _consistent_kind = if sc.is_final {
244             ConsistentKind::FinalClass
245         } else if sc.user_attributes.iter().any(|UserAttribute { name, .. }| {
246             name.id() == self.special_names.user_attributes.uaConsistentConstruct
247         }) {
248             ConsistentKind::ConsistentConstruct
249         } else {
250             ConsistentKind::Inconsistent
251         };
253         match &sc.constructor {
254             None => {}
255             Some(sm) => {
256                 let cls = sc.name.id();
257                 let vis = self.visibility(cls, sc.module.as_ref(), sm.visibility);
258                 let meth_flags = &sm.flags;
259                 let flag_args = ClassEltFlagsArgs {
260                     xhp_attr: None,
261                     is_abstract: meth_flags.is_abstract(),
262                     is_final: meth_flags.is_final(),
263                     is_superfluous_override: false,
264                     is_lsb: false,
265                     is_synthesized: false,
266                     is_const: false,
267                     is_lateinit: false,
268                     is_dynamicallycallable: false,
269                     is_readonly_prop: false,
270                     supports_dynamic_type: false,
271                     needs_init: false,
272                 };
273                 let elt = FoldedElement {
274                     origin: sc.name.id(),
275                     visibility: vis,
276                     deprecated: sm.deprecated,
277                     flags: ClassEltFlags::new(flag_args),
278                 };
279                 *constructor = Some(elt)
280             }
281         }
282     }
284     fn get_implements(
285         &self,
286         parents: &TypeNameMap<Arc<FoldedClass<R>>>,
287         ty: &DeclTy<R>,
288         inst: &mut TypeNameMap<DeclTy<R>>,
289     ) {
290         match ty.unwrap_class_type() {
291             None => {}
292             Some((_, pos_id, tyl)) => match parents.get(&pos_id.id()) {
293                 None => {
294                     inst.insert(pos_id.id(), ty.clone());
295                 }
296                 Some(cls) => {
297                     let subst = Subst::new((), tyl);
298                     for (&anc_name, anc_ty) in &cls.ancestors {
299                         inst.insert(anc_name, subst.instantiate(anc_ty));
300                     }
301                     inst.insert(pos_id.id(), ty.clone());
302                 }
303             },
304         }
305     }
307     fn decl_class_impl(
308         &self,
309         sc: &ShallowClass<R>,
310         parents: &TypeNameMap<Arc<FoldedClass<R>>>,
311     ) -> Arc<FoldedClass<R>> {
312         let inh = Inherited::make(sc, parents);
314         let mut props = inh.props;
315         sc.props
316             .iter()
317             .for_each(|sp| self.decl_prop(&mut props, sc, sp));
319         let mut static_props = inh.static_props;
320         sc.static_props
321             .iter()
322             .for_each(|ssp| self.decl_static_prop(&mut static_props, sc, ssp));
324         let mut methods = inh.methods;
325         sc.methods
326             .iter()
327             .for_each(|sm| self.decl_method(&mut methods, sc, sm));
329         let mut static_methods = inh.static_methods;
330         sc.static_methods
331             .iter()
332             .for_each(|sm| self.decl_method(&mut static_methods, sc, sm));
334         let mut constructor = inh.constructor;
335         self.decl_constructor(&mut constructor, sc);
337         let mut ancestors = Default::default();
338         sc.extends
339             .iter()
340             .for_each(|ty| self.get_implements(parents, ty, &mut ancestors));
342         let mut consts = inh.consts;
343         consts.insert(
344             ClassConstName(self.special_names.members.mClass),
345             self.decl_class_class(&sc.name),
346         );
348         Arc::new(FoldedClass {
349             name: sc.name.id(),
350             pos: sc.name.pos().clone(),
351             substs: inh.substs,
352             ancestors,
353             props,
354             static_props,
355             methods,
356             static_methods,
357             constructor,
358             consts,
359         })
360     }
362     fn decl_class(&self, stack: &mut TypeNameSet, name: TypeName) -> Option<Arc<FoldedClass<R>>> {
363         let shallow_class = self.shallow_decl_provider.get_shallow_class(name)?;
364         stack.insert(name);
365         let parents = self.decl_class_parents(stack, &shallow_class);
366         Some(self.decl_class_impl(&shallow_class, &parents))
367     }
369     fn get_folded_class_impl(
370         &self,
371         stack: &mut TypeNameSet,
372         name: TypeName,
373     ) -> Option<Arc<FoldedClass<R>>> {
374         match self.cache.get_folded_class(name) {
375             Some(rc) => Some(rc),
376             None => match self.decl_class(stack, name) {
377                 None => None,
378                 Some(rc) => {
379                     self.cache.put_folded_class(name, Arc::clone(&rc));
380                     Some(rc)
381                 }
382             },
383         }
384     }