ra_ap_hir_def/data/
adt.rs

1//! Defines hir-level representation of structs, enums and unions
2
3use base_db::Crate;
4use bitflags::bitflags;
5use cfg::CfgOptions;
6
7use hir_expand::name::Name;
8use intern::sym;
9use la_arena::Arena;
10use rustc_abi::{IntegerType, ReprOptions};
11use triomphe::Arc;
12
13use crate::{
14    EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
15    db::DefDatabase,
16    hir::Expr,
17    item_tree::{
18        AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId,
19    },
20    lang_item::LangItem,
21    nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
22    type_ref::{TypeRefId, TypesMap},
23    visibility::RawVisibility,
24};
25
26/// Note that we use `StructData` for unions as well!
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct StructData {
29    pub name: Name,
30    pub repr: Option<ReprOptions>,
31    pub visibility: RawVisibility,
32    pub flags: StructFlags,
33}
34
35bitflags! {
36    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
37    pub struct StructFlags: u8 {
38        const NO_FLAGS         = 0;
39        /// Indicates whether the struct is `PhantomData`.
40        const IS_PHANTOM_DATA  = 1 << 2;
41        /// Indicates whether the struct has a `#[fundamental]` attribute.
42        const IS_FUNDAMENTAL   = 1 << 3;
43        // FIXME: should this be a flag?
44        /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
45        const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL      = 1 << 4;
46        /// Indicates whether this struct is `Box`.
47        const IS_BOX           = 1 << 5;
48        /// Indicates whether this struct is `ManuallyDrop`.
49        const IS_MANUALLY_DROP = 1 << 6;
50        /// Indicates whether this struct is `UnsafeCell`.
51        const IS_UNSAFE_CELL   = 1 << 7;
52    }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct EnumData {
57    pub name: Name,
58    pub repr: Option<ReprOptions>,
59    pub visibility: RawVisibility,
60    pub rustc_has_incoherent_inherent_impls: bool,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct EnumVariants {
65    pub variants: Box<[(EnumVariantId, Name)]>,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct EnumVariantData {
70    pub name: Name,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum VariantData {
75    Record { fields: Arena<FieldData>, types_map: Arc<TypesMap> },
76    Tuple { fields: Arena<FieldData>, types_map: Arc<TypesMap> },
77    Unit,
78}
79
80impl VariantData {
81    #[inline]
82    pub(crate) fn variant_data_query(db: &dyn DefDatabase, id: VariantId) -> Arc<VariantData> {
83        db.variant_data_with_diagnostics(id).0
84    }
85
86    pub(crate) fn variant_data_with_diagnostics_query(
87        db: &dyn DefDatabase,
88        id: VariantId,
89    ) -> (Arc<VariantData>, DefDiagnostics) {
90        let (shape, types_map, (fields, diagnostics)) = match id {
91            VariantId::EnumVariantId(id) => {
92                let loc = id.lookup(db);
93                let item_tree = loc.id.item_tree(db);
94                let parent = loc.parent.lookup(db);
95                let krate = parent.container.krate;
96                let variant = &item_tree[loc.id.value];
97                (
98                    variant.shape,
99                    variant.types_map.clone(),
100                    lower_fields(
101                        db,
102                        krate,
103                        parent.container.local_id,
104                        loc.id.tree_id(),
105                        &item_tree,
106                        krate.cfg_options(db),
107                        FieldParent::EnumVariant(loc.id.value),
108                        &variant.fields,
109                        Some(item_tree[parent.id.value].visibility),
110                    ),
111                )
112            }
113            VariantId::StructId(id) => {
114                let loc = id.lookup(db);
115                let item_tree = loc.id.item_tree(db);
116                let krate = loc.container.krate;
117                let strukt = &item_tree[loc.id.value];
118                (
119                    strukt.shape,
120                    strukt.types_map.clone(),
121                    lower_fields(
122                        db,
123                        krate,
124                        loc.container.local_id,
125                        loc.id.tree_id(),
126                        &item_tree,
127                        krate.cfg_options(db),
128                        FieldParent::Struct(loc.id.value),
129                        &strukt.fields,
130                        None,
131                    ),
132                )
133            }
134            VariantId::UnionId(id) => {
135                let loc = id.lookup(db);
136                let item_tree = loc.id.item_tree(db);
137                let krate = loc.container.krate;
138                let union = &item_tree[loc.id.value];
139                (
140                    FieldsShape::Record,
141                    union.types_map.clone(),
142                    lower_fields(
143                        db,
144                        krate,
145                        loc.container.local_id,
146                        loc.id.tree_id(),
147                        &item_tree,
148                        krate.cfg_options(db),
149                        FieldParent::Union(loc.id.value),
150                        &union.fields,
151                        None,
152                    ),
153                )
154            }
155        };
156
157        (
158            Arc::new(match shape {
159                FieldsShape::Record => VariantData::Record { fields, types_map },
160                FieldsShape::Tuple => VariantData::Tuple { fields, types_map },
161                FieldsShape::Unit => VariantData::Unit,
162            }),
163            DefDiagnostics::new(diagnostics),
164        )
165    }
166}
167
168/// A single field of an enum variant or struct
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct FieldData {
171    pub name: Name,
172    pub type_ref: TypeRefId,
173    pub visibility: RawVisibility,
174    pub is_unsafe: bool,
175}
176
177fn repr_from_value(
178    db: &dyn DefDatabase,
179    krate: Crate,
180    item_tree: &ItemTree,
181    of: AttrOwner,
182) -> Option<ReprOptions> {
183    item_tree.attrs(db, krate, of).repr()
184}
185
186impl StructData {
187    #[inline]
188    pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
189        let loc = id.lookup(db);
190        let krate = loc.container.krate;
191        let item_tree = loc.id.item_tree(db);
192        let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
193        let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
194
195        let mut flags = StructFlags::NO_FLAGS;
196        if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
197            flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
198        }
199        if attrs.by_key(&sym::fundamental).exists() {
200            flags |= StructFlags::IS_FUNDAMENTAL;
201        }
202        if let Some(lang) = attrs.lang_item() {
203            match lang {
204                LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
205                LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
206                LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
207                LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
208                _ => (),
209            }
210        }
211
212        let strukt = &item_tree[loc.id.value];
213        Arc::new(StructData {
214            name: strukt.name.clone(),
215            repr,
216            visibility: item_tree[strukt.visibility].clone(),
217            flags,
218        })
219    }
220
221    #[inline]
222    pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
223        let loc = id.lookup(db);
224        let krate = loc.container.krate;
225        let item_tree = loc.id.item_tree(db);
226        let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
227        let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
228        let mut flags = StructFlags::NO_FLAGS;
229
230        if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
231            flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
232        }
233        if attrs.by_key(&sym::fundamental).exists() {
234            flags |= StructFlags::IS_FUNDAMENTAL;
235        }
236
237        let union = &item_tree[loc.id.value];
238
239        Arc::new(StructData {
240            name: union.name.clone(),
241            repr,
242            visibility: item_tree[union.visibility].clone(),
243            flags,
244        })
245    }
246}
247
248impl EnumVariants {
249    pub(crate) fn enum_variants_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumVariants> {
250        let loc = e.lookup(db);
251        let item_tree = loc.id.item_tree(db);
252
253        Arc::new(EnumVariants {
254            variants: loc.container.def_map(db).enum_definitions[&e]
255                .iter()
256                .map(|&id| (id, item_tree[id.lookup(db).id.value].name.clone()))
257                .collect(),
258        })
259    }
260
261    pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
262        let &(id, _) = self.variants.iter().find(|(_id, n)| n == name)?;
263        Some(id)
264    }
265
266    // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
267    pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
268        self.variants.iter().all(|&(v, _)| {
269            // The condition check order is slightly modified from rustc
270            // to improve performance by early returning with relatively fast checks
271            let variant = &db.variant_data(v.into());
272            if !variant.fields().is_empty() {
273                return false;
274            }
275            // The outer if condition is whether this variant has const ctor or not
276            if !matches!(variant.kind(), StructKind::Unit) {
277                let body = db.body(v.into());
278                // A variant with explicit discriminant
279                if body.exprs[body.body_expr] != Expr::Missing {
280                    return false;
281                }
282            }
283            true
284        })
285    }
286}
287
288impl EnumData {
289    pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
290        let loc = e.lookup(db);
291        let krate = loc.container.krate;
292        let item_tree = loc.id.item_tree(db);
293        let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
294        let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
295
296        let rustc_has_incoherent_inherent_impls =
297            attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists();
298
299        let enum_ = &item_tree[loc.id.value];
300
301        Arc::new(EnumData {
302            name: enum_.name.clone(),
303            repr,
304            visibility: item_tree[enum_.visibility].clone(),
305            rustc_has_incoherent_inherent_impls,
306        })
307    }
308
309    pub fn variant_body_type(&self) -> IntegerType {
310        match self.repr {
311            Some(ReprOptions { int: Some(builtin), .. }) => builtin,
312            _ => IntegerType::Pointer(true),
313        }
314    }
315}
316
317impl EnumVariantData {
318    #[inline]
319    pub(crate) fn enum_variant_data_query(
320        db: &dyn DefDatabase,
321        e: EnumVariantId,
322    ) -> Arc<EnumVariantData> {
323        let loc = e.lookup(db);
324        let item_tree = loc.id.item_tree(db);
325        let variant = &item_tree[loc.id.value];
326
327        Arc::new(EnumVariantData { name: variant.name.clone() })
328    }
329}
330
331impl VariantData {
332    pub fn fields(&self) -> &Arena<FieldData> {
333        const EMPTY: &Arena<FieldData> = &Arena::new();
334        match self {
335            VariantData::Record { fields, .. } | VariantData::Tuple { fields, .. } => fields,
336            _ => EMPTY,
337        }
338    }
339
340    pub fn types_map(&self) -> &TypesMap {
341        match self {
342            VariantData::Record { types_map, .. } | VariantData::Tuple { types_map, .. } => {
343                types_map
344            }
345            VariantData::Unit => TypesMap::EMPTY,
346        }
347    }
348
349    // FIXME: Linear lookup
350    pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
351        self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
352    }
353
354    pub fn kind(&self) -> StructKind {
355        match self {
356            VariantData::Record { .. } => StructKind::Record,
357            VariantData::Tuple { .. } => StructKind::Tuple,
358            VariantData::Unit => StructKind::Unit,
359        }
360    }
361}
362
363#[derive(Debug, Copy, Clone, PartialEq, Eq)]
364pub enum StructKind {
365    Tuple,
366    Record,
367    Unit,
368}
369
370fn lower_fields(
371    db: &dyn DefDatabase,
372    krate: Crate,
373    container: LocalModuleId,
374    tree_id: TreeId,
375    item_tree: &ItemTree,
376    cfg_options: &CfgOptions,
377    parent: FieldParent,
378    fields: &[Field],
379    override_visibility: Option<RawVisibilityId>,
380) -> (Arena<FieldData>, Vec<DefDiagnostic>) {
381    let mut diagnostics = Vec::new();
382    let mut arena = Arena::new();
383    for (idx, field) in fields.iter().enumerate() {
384        let attr_owner = AttrOwner::make_field_indexed(parent, idx);
385        let attrs = item_tree.attrs(db, krate, attr_owner);
386        if attrs.is_cfg_enabled(cfg_options) {
387            arena.alloc(lower_field(item_tree, field, override_visibility));
388        } else {
389            diagnostics.push(DefDiagnostic::unconfigured_code(
390                container,
391                tree_id,
392                attr_owner,
393                attrs.cfg().unwrap(),
394                cfg_options.clone(),
395            ))
396        }
397    }
398    (arena, diagnostics)
399}
400
401fn lower_field(
402    item_tree: &ItemTree,
403    field: &Field,
404    override_visibility: Option<RawVisibilityId>,
405) -> FieldData {
406    FieldData {
407        name: field.name.clone(),
408        type_ref: field.type_ref,
409        visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
410        is_unsafe: field.is_unsafe,
411    }
412}