1 // Copyright (c) Facebook, Inc. and its affiliates.
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;
18 ClassConstName, MethodNameMap, ModuleName, Positioned, PropNameMap, TypeName, TypeNameMap,
23 // note(sf, 2022-02-03): c.f. hphp/hack/src/decl/decl_folded_class.ml
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> {
35 cache: Arc<dyn FoldedDeclCache<Reason = R>>,
36 alloc: &'static Allocator<R>,
37 special_names: &'static SpecialNames,
38 shallow_decl_provider: Arc<ShallowDeclProvider<R>>,
44 shallow_decl_provider,
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)
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");
63 module: Option<&Positioned<ModuleName, R::Pos>>,
65 ) -> CeVisibility<R::Pos> {
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())
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(
86 Positioned::new(pos.clone(), self.special_names.classes.cClassname),
87 vec![self.alloc.decl_ty(reason, DeclTy_::DTthis)],
92 kind: ClassConstKind::CCConcrete,
102 props: &mut PropNameMap<FoldedElement<R>>,
103 sc: &ShallowClass<R>,
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(),
115 is_superfluous_override: 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(),
125 let elt = FoldedElement {
126 origin: sc.name.id(),
129 flags: ClassEltFlags::new(flag_args),
131 props.insert(prop, elt);
136 static_props: &mut PropNameMap<FoldedElement<R>>,
137 sc: &ShallowClass<R>,
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(),
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(),
158 let elt = FoldedElement {
159 origin: sc.name.id(),
162 flags: ClassEltFlags::new(flag_args),
164 static_props.insert(prop, elt);
169 methods: &mut MethodNameMap<FoldedElement<R>>,
170 sc: &ShallowClass<R>,
171 sm: &ShallowMethod<R>,
173 let cls = sc.name.id();
174 let meth = sm.name.id();
175 let vis = match (methods.get(&meth), sm.visibility) {
178 visibility: CeVisibility::Protected(cls),
181 Visibility::Protected,
182 ) => CeVisibility::Protected(*cls),
183 (_, v) => self.visibility(cls, sc.module.as_ref(), v),
186 let meth_flags = &sm.flags;
187 let flag_args = ClassEltFlagsArgs {
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),
193 is_synthesized: false,
196 is_dynamicallycallable: meth_flags.is_dynamicallycallable(),
197 is_readonly_prop: false,
198 supports_dynamic_type: meth_flags.supports_dynamic_type(),
201 let elt = FoldedElement {
204 deprecated: sm.deprecated,
205 flags: ClassEltFlags::new(flag_args),
208 methods.insert(meth, elt);
213 stack: &mut TypeNameSet,
214 acc: &mut TypeNameMap<Arc<FoldedClass<R>>>,
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);
229 fn decl_class_parents(
231 stack: &mut TypeNameSet,
232 sc: &ShallowClass<R>,
233 ) -> TypeNameMap<Arc<FoldedClass<R>>> {
234 let mut acc = Default::default();
237 .for_each(|ty| self.decl_class_type(stack, &mut acc, ty));
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
248 ConsistentKind::ConsistentConstruct
250 ConsistentKind::Inconsistent
253 match &sc.constructor {
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 {
261 is_abstract: meth_flags.is_abstract(),
262 is_final: meth_flags.is_final(),
263 is_superfluous_override: false,
265 is_synthesized: false,
268 is_dynamicallycallable: false,
269 is_readonly_prop: false,
270 supports_dynamic_type: false,
273 let elt = FoldedElement {
274 origin: sc.name.id(),
276 deprecated: sm.deprecated,
277 flags: ClassEltFlags::new(flag_args),
279 *constructor = Some(elt)
286 parents: &TypeNameMap<Arc<FoldedClass<R>>>,
288 inst: &mut TypeNameMap<DeclTy<R>>,
290 match ty.unwrap_class_type() {
292 Some((_, pos_id, tyl)) => match parents.get(&pos_id.id()) {
294 inst.insert(pos_id.id(), ty.clone());
297 let subst = Subst::new((), tyl);
298 for (&anc_name, anc_ty) in &cls.ancestors {
299 inst.insert(anc_name, subst.instantiate(anc_ty));
301 inst.insert(pos_id.id(), ty.clone());
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;
317 .for_each(|sp| self.decl_prop(&mut props, sc, sp));
319 let mut static_props = inh.static_props;
322 .for_each(|ssp| self.decl_static_prop(&mut static_props, sc, ssp));
324 let mut methods = inh.methods;
327 .for_each(|sm| self.decl_method(&mut methods, sc, sm));
329 let mut static_methods = inh.static_methods;
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();
340 .for_each(|ty| self.get_implements(parents, ty, &mut ancestors));
342 let mut consts = inh.consts;
344 ClassConstName(self.special_names.members.mClass),
345 self.decl_class_class(&sc.name),
348 Arc::new(FoldedClass {
350 pos: sc.name.pos().clone(),
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)?;
365 let parents = self.decl_class_parents(stack, &shallow_class);
366 Some(self.decl_class_impl(&shallow_class, &parents))
369 fn get_folded_class_impl(
371 stack: &mut TypeNameSet,
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) {
379 self.cache.put_folded_class(name, Arc::clone(&rc));