hir_def/
signatures.rs

1//! Item signature IR definitions
2
3use std::{cell::LazyCell, ops::Not as _};
4
5use bitflags::bitflags;
6use cfg::{CfgExpr, CfgOptions};
7use hir_expand::{
8    InFile, Intern, Lookup,
9    name::{AsName, Name},
10};
11use intern::{Symbol, sym};
12use la_arena::{Arena, Idx};
13use rustc_abi::{IntegerType, ReprOptions};
14use syntax::{
15    NodeOrToken, SyntaxNodePtr, T,
16    ast::{self, HasGenericParams, HasName, HasVisibility, IsString},
17};
18use thin_vec::ThinVec;
19use triomphe::Arc;
20
21use crate::{
22    ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId,
23    ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
24    attr::Attrs,
25    db::DefDatabase,
26    expr_store::{
27        ExpressionStore, ExpressionStoreSourceMap,
28        lower::{
29            ExprCollector, lower_function, lower_generic_params, lower_trait, lower_type_alias,
30        },
31    },
32    hir::{ExprId, PatId, generics::GenericParams},
33    item_tree::{FieldsShape, RawVisibility, visibility_from_ast},
34    lang_item::LangItem,
35    src::HasSource,
36    type_ref::{TraitRef, TypeBound, TypeRefId},
37};
38
39#[inline]
40fn as_name_opt(name: Option<ast::Name>) -> Name {
41    name.map_or_else(Name::missing, |it| it.as_name())
42}
43
44#[derive(Debug, PartialEq, Eq)]
45pub struct StructSignature {
46    pub name: Name,
47    pub generic_params: Arc<GenericParams>,
48    pub store: Arc<ExpressionStore>,
49    pub flags: StructFlags,
50    pub shape: FieldsShape,
51    pub repr: Option<ReprOptions>,
52}
53
54bitflags! {
55    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
56    pub struct StructFlags: u8 {
57        /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
58        const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
59        /// Indicates whether the struct has a `#[fundamental]` attribute.
60        const FUNDAMENTAL      = 1 << 2;
61        /// Indicates whether the struct is `PhantomData`.
62        const IS_PHANTOM_DATA  = 1 << 3;
63        /// Indicates whether this struct is `Box`.
64        const IS_BOX           = 1 << 4;
65        /// Indicates whether this struct is `ManuallyDrop`.
66        const IS_MANUALLY_DROP = 1 << 5;
67        /// Indicates whether this struct is `UnsafeCell`.
68        const IS_UNSAFE_CELL   = 1 << 6;
69        /// Indicates whether this struct is `UnsafePinned`.
70        const IS_UNSAFE_PINNED = 1 << 7;
71    }
72}
73
74impl StructSignature {
75    pub fn query(db: &dyn DefDatabase, id: StructId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
76        let loc = id.lookup(db);
77        let InFile { file_id, value: source } = loc.source(db);
78        let attrs = db.attrs(id.into());
79
80        let mut flags = StructFlags::empty();
81        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
82            flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
83        }
84        if attrs.by_key(sym::fundamental).exists() {
85            flags |= StructFlags::FUNDAMENTAL;
86        }
87        if let Some(lang) = attrs.lang_item() {
88            match lang {
89                LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
90                LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
91                LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
92                LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
93                LangItem::UnsafePinned => flags |= StructFlags::IS_UNSAFE_PINNED,
94                _ => (),
95            }
96        }
97        let repr = attrs.repr();
98        let shape = adt_shape(source.kind());
99
100        let (store, generic_params, source_map) = lower_generic_params(
101            db,
102            loc.container,
103            id.into(),
104            file_id,
105            source.generic_param_list(),
106            source.where_clause(),
107        );
108        (
109            Arc::new(StructSignature {
110                generic_params,
111                store,
112                flags,
113                shape,
114                name: as_name_opt(source.name()),
115                repr,
116            }),
117            Arc::new(source_map),
118        )
119    }
120}
121
122#[inline]
123fn adt_shape(adt_kind: ast::StructKind) -> FieldsShape {
124    match adt_kind {
125        ast::StructKind::Record(_) => FieldsShape::Record,
126        ast::StructKind::Tuple(_) => FieldsShape::Tuple,
127        ast::StructKind::Unit => FieldsShape::Unit,
128    }
129}
130
131#[derive(Debug, PartialEq, Eq)]
132pub struct UnionSignature {
133    pub name: Name,
134    pub generic_params: Arc<GenericParams>,
135    pub store: Arc<ExpressionStore>,
136    pub flags: StructFlags,
137    pub repr: Option<ReprOptions>,
138}
139
140impl UnionSignature {
141    pub fn query(db: &dyn DefDatabase, id: UnionId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
142        let loc = id.lookup(db);
143        let attrs = db.attrs(id.into());
144        let mut flags = StructFlags::empty();
145        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
146            flags |= StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
147        }
148        if attrs.by_key(sym::fundamental).exists() {
149            flags |= StructFlags::FUNDAMENTAL;
150        }
151
152        let repr = attrs.repr();
153
154        let InFile { file_id, value: source } = loc.source(db);
155        let (store, generic_params, source_map) = lower_generic_params(
156            db,
157            loc.container,
158            id.into(),
159            file_id,
160            source.generic_param_list(),
161            source.where_clause(),
162        );
163        (
164            Arc::new(UnionSignature {
165                generic_params,
166                store,
167                flags,
168                repr,
169                name: as_name_opt(source.name()),
170            }),
171            Arc::new(source_map),
172        )
173    }
174}
175
176bitflags! {
177    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
178    pub struct EnumFlags: u8 {
179        const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS  = 1 << 1;
180    }
181}
182
183#[derive(Debug, PartialEq, Eq)]
184pub struct EnumSignature {
185    pub name: Name,
186    pub generic_params: Arc<GenericParams>,
187    pub store: Arc<ExpressionStore>,
188    pub flags: EnumFlags,
189    pub repr: Option<ReprOptions>,
190}
191
192impl EnumSignature {
193    pub fn query(db: &dyn DefDatabase, id: EnumId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
194        let loc = id.lookup(db);
195        let attrs = db.attrs(id.into());
196        let mut flags = EnumFlags::empty();
197        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
198            flags |= EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
199        }
200
201        let repr = attrs.repr();
202
203        let InFile { file_id, value: source } = loc.source(db);
204        let (store, generic_params, source_map) = lower_generic_params(
205            db,
206            loc.container,
207            id.into(),
208            file_id,
209            source.generic_param_list(),
210            source.where_clause(),
211        );
212
213        (
214            Arc::new(EnumSignature {
215                generic_params,
216                store,
217                flags,
218                repr,
219                name: as_name_opt(source.name()),
220            }),
221            Arc::new(source_map),
222        )
223    }
224
225    pub fn variant_body_type(&self) -> IntegerType {
226        match self.repr {
227            Some(ReprOptions { int: Some(builtin), .. }) => builtin,
228            _ => IntegerType::Pointer(true),
229        }
230    }
231}
232bitflags::bitflags! {
233    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
234    pub struct ConstFlags: u8 {
235        const HAS_BODY = 1 << 1;
236        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
237    }
238}
239
240#[derive(Debug, PartialEq, Eq)]
241pub struct ConstSignature {
242    pub name: Option<Name>,
243    // generic_params: Arc<GenericParams>,
244    pub store: Arc<ExpressionStore>,
245    pub type_ref: TypeRefId,
246    pub flags: ConstFlags,
247}
248
249impl ConstSignature {
250    pub fn query(db: &dyn DefDatabase, id: ConstId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
251        let loc = id.lookup(db);
252
253        let module = loc.container.module(db);
254        let attrs = db.attrs(id.into());
255        let mut flags = ConstFlags::empty();
256        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
257            flags |= ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
258        }
259        let source = loc.source(db);
260        if source.value.body().is_some() {
261            flags.insert(ConstFlags::HAS_BODY);
262        }
263
264        let (store, source_map, type_ref) =
265            crate::expr_store::lower::lower_type_ref(db, module, source.as_ref().map(|it| it.ty()));
266
267        (
268            Arc::new(ConstSignature {
269                store: Arc::new(store),
270                type_ref,
271                flags,
272                name: source.value.name().map(|it| it.as_name()),
273            }),
274            Arc::new(source_map),
275        )
276    }
277
278    pub fn has_body(&self) -> bool {
279        self.flags.contains(ConstFlags::HAS_BODY)
280    }
281}
282
283bitflags::bitflags! {
284    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
285    pub struct StaticFlags: u8 {
286        const HAS_BODY = 1 << 1;
287        const MUTABLE    = 1 << 3;
288        const UNSAFE     = 1 << 4;
289        const EXPLICIT_SAFE = 1 << 5;
290        const EXTERN     = 1 << 6;
291        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
292    }
293}
294
295#[derive(Debug, PartialEq, Eq)]
296pub struct StaticSignature {
297    pub name: Name,
298
299    // generic_params: Arc<GenericParams>,
300    pub store: Arc<ExpressionStore>,
301    pub type_ref: TypeRefId,
302    pub flags: StaticFlags,
303}
304impl StaticSignature {
305    pub fn query(db: &dyn DefDatabase, id: StaticId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
306        let loc = id.lookup(db);
307
308        let module = loc.container.module(db);
309        let attrs = db.attrs(id.into());
310        let mut flags = StaticFlags::empty();
311        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
312            flags |= StaticFlags::RUSTC_ALLOW_INCOHERENT_IMPL;
313        }
314
315        if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
316            flags.insert(StaticFlags::EXTERN);
317        }
318
319        let source = loc.source(db);
320        if source.value.body().is_some() {
321            flags.insert(StaticFlags::HAS_BODY);
322        }
323        if source.value.mut_token().is_some() {
324            flags.insert(StaticFlags::MUTABLE);
325        }
326        if source.value.unsafe_token().is_some() {
327            flags.insert(StaticFlags::UNSAFE);
328        }
329        if source.value.safe_token().is_some() {
330            flags.insert(StaticFlags::EXPLICIT_SAFE);
331        }
332
333        let (store, source_map, type_ref) =
334            crate::expr_store::lower::lower_type_ref(db, module, source.as_ref().map(|it| it.ty()));
335
336        (
337            Arc::new(StaticSignature {
338                store: Arc::new(store),
339                type_ref,
340                flags,
341                name: as_name_opt(source.value.name()),
342            }),
343            Arc::new(source_map),
344        )
345    }
346}
347
348bitflags::bitflags! {
349    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
350    pub struct ImplFlags: u8 {
351        const NEGATIVE = 1 << 1;
352        const UNSAFE = 1 << 3;
353    }
354}
355
356#[derive(Debug, PartialEq, Eq)]
357pub struct ImplSignature {
358    pub generic_params: Arc<GenericParams>,
359    pub store: Arc<ExpressionStore>,
360    pub self_ty: TypeRefId,
361    pub target_trait: Option<TraitRef>,
362    pub flags: ImplFlags,
363}
364
365impl ImplSignature {
366    pub fn query(db: &dyn DefDatabase, id: ImplId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
367        let loc = id.lookup(db);
368
369        let mut flags = ImplFlags::empty();
370        let src = loc.source(db);
371        if src.value.unsafe_token().is_some() {
372            flags.insert(ImplFlags::UNSAFE);
373        }
374        if src.value.excl_token().is_some() {
375            flags.insert(ImplFlags::NEGATIVE);
376        }
377
378        let (store, source_map, self_ty, target_trait, generic_params) =
379            crate::expr_store::lower::lower_impl(db, loc.container, src, id);
380
381        (
382            Arc::new(ImplSignature {
383                store: Arc::new(store),
384                generic_params,
385                self_ty,
386                target_trait,
387                flags,
388            }),
389            Arc::new(source_map),
390        )
391    }
392}
393
394bitflags::bitflags! {
395    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
396    pub struct TraitFlags: u16 {
397        const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
398        const FUNDAMENTAL = 1 << 2;
399        const UNSAFE = 1 << 3;
400        const AUTO = 1 << 4;
401        const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5;
402        const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6;
403        const RUSTC_PAREN_SUGAR = 1 << 7;
404        const COINDUCTIVE = 1 << 8;
405        const ALIAS = 1 << 9;
406    }
407}
408
409#[derive(Debug, PartialEq, Eq)]
410pub struct TraitSignature {
411    pub name: Name,
412    pub generic_params: Arc<GenericParams>,
413    pub store: Arc<ExpressionStore>,
414    pub flags: TraitFlags,
415}
416
417impl TraitSignature {
418    pub fn query(db: &dyn DefDatabase, id: TraitId) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
419        let loc = id.lookup(db);
420
421        let mut flags = TraitFlags::empty();
422        let attrs = db.attrs(id.into());
423        let source = loc.source(db);
424        if source.value.auto_token().is_some() {
425            flags.insert(TraitFlags::AUTO);
426        }
427        if source.value.unsafe_token().is_some() {
428            flags.insert(TraitFlags::UNSAFE);
429        }
430        if source.value.eq_token().is_some() {
431            flags.insert(TraitFlags::ALIAS);
432        }
433        if attrs.by_key(sym::fundamental).exists() {
434            flags |= TraitFlags::FUNDAMENTAL;
435        }
436        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
437            flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
438        }
439        if attrs.by_key(sym::rustc_paren_sugar).exists() {
440            flags |= TraitFlags::RUSTC_PAREN_SUGAR;
441        }
442        if attrs.by_key(sym::rustc_coinductive).exists() {
443            flags |= TraitFlags::COINDUCTIVE;
444        }
445        let mut skip_array_during_method_dispatch =
446            attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists();
447        let mut skip_boxed_slice_during_method_dispatch = false;
448        for tt in attrs.by_key(sym::rustc_skip_during_method_dispatch).tt_values() {
449            for tt in tt.iter() {
450                if let tt::iter::TtElement::Leaf(tt::Leaf::Ident(ident)) = tt {
451                    skip_array_during_method_dispatch |= ident.sym == sym::array;
452                    skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice;
453                }
454            }
455        }
456
457        if skip_array_during_method_dispatch {
458            flags |= TraitFlags::SKIP_ARRAY_DURING_METHOD_DISPATCH;
459        }
460        if skip_boxed_slice_during_method_dispatch {
461            flags |= TraitFlags::SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH;
462        }
463
464        let name = as_name_opt(source.value.name());
465        let (store, source_map, generic_params) = lower_trait(db, loc.container, source, id);
466
467        (
468            Arc::new(TraitSignature { store: Arc::new(store), generic_params, flags, name }),
469            Arc::new(source_map),
470        )
471    }
472}
473
474bitflags! {
475    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
476    pub struct FnFlags: u16 {
477        const HAS_BODY = 1 << 1;
478        const DEFAULT = 1 << 2;
479        const CONST = 1 << 3;
480        const ASYNC = 1 << 4;
481        const UNSAFE = 1 << 5;
482        const HAS_VARARGS = 1 << 6;
483        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
484        const HAS_SELF_PARAM = 1 << 8;
485        /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396),
486        /// but keeping it for all functions will consume a lot of memory when there are
487        /// only very few functions with it. So we only encode its existence here, and lookup
488        /// it if needed.
489        const HAS_TARGET_FEATURE = 1 << 9;
490        const DEPRECATED_SAFE_2024 = 1 << 10;
491        const EXPLICIT_SAFE = 1 << 11;
492    }
493}
494
495#[derive(Debug, PartialEq, Eq)]
496pub struct FunctionSignature {
497    pub name: Name,
498    pub generic_params: Arc<GenericParams>,
499    pub store: Arc<ExpressionStore>,
500    pub params: Box<[TypeRefId]>,
501    pub ret_type: Option<TypeRefId>,
502    pub abi: Option<Symbol>,
503    pub flags: FnFlags,
504    // FIXME: we should put this behind a fn flags + query to avoid bloating the struct
505    pub legacy_const_generics_indices: Option<Box<Box<[u32]>>>,
506}
507
508impl FunctionSignature {
509    pub fn query(
510        db: &dyn DefDatabase,
511        id: FunctionId,
512    ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
513        let loc = id.lookup(db);
514        let module = loc.container.module(db);
515
516        let mut flags = FnFlags::empty();
517        let attrs = db.attrs(id.into());
518        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
519            flags.insert(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
520        }
521
522        if attrs.by_key(sym::target_feature).exists() {
523            flags.insert(FnFlags::HAS_TARGET_FEATURE);
524        }
525        let legacy_const_generics_indices = attrs.rustc_legacy_const_generics();
526
527        let source = loc.source(db);
528
529        if source.value.unsafe_token().is_some() {
530            if attrs.by_key(sym::rustc_deprecated_safe_2024).exists() {
531                flags.insert(FnFlags::DEPRECATED_SAFE_2024);
532            } else {
533                flags.insert(FnFlags::UNSAFE);
534            }
535        }
536        if source.value.async_token().is_some() {
537            flags.insert(FnFlags::ASYNC);
538        }
539        if source.value.const_token().is_some() {
540            flags.insert(FnFlags::CONST);
541        }
542        if source.value.default_token().is_some() {
543            flags.insert(FnFlags::DEFAULT);
544        }
545        if source.value.safe_token().is_some() {
546            flags.insert(FnFlags::EXPLICIT_SAFE);
547        }
548        if source.value.body().is_some() {
549            flags.insert(FnFlags::HAS_BODY);
550        }
551
552        let name = as_name_opt(source.value.name());
553        let abi = source.value.abi().map(|abi| {
554            abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes()))
555        });
556        let (store, source_map, generic_params, params, ret_type, self_param, variadic) =
557            lower_function(db, module, source, id);
558        if self_param {
559            flags.insert(FnFlags::HAS_SELF_PARAM);
560        }
561        if variadic {
562            flags.insert(FnFlags::HAS_VARARGS);
563        }
564        (
565            Arc::new(FunctionSignature {
566                generic_params,
567                store: Arc::new(store),
568                params,
569                ret_type,
570                abi,
571                flags,
572                legacy_const_generics_indices,
573                name,
574            }),
575            Arc::new(source_map),
576        )
577    }
578
579    pub fn has_body(&self) -> bool {
580        self.flags.contains(FnFlags::HAS_BODY)
581    }
582
583    /// True if the first param is `self`. This is relevant to decide whether this
584    /// can be called as a method.
585    pub fn has_self_param(&self) -> bool {
586        self.flags.contains(FnFlags::HAS_SELF_PARAM)
587    }
588
589    pub fn is_default(&self) -> bool {
590        self.flags.contains(FnFlags::DEFAULT)
591    }
592
593    pub fn is_const(&self) -> bool {
594        self.flags.contains(FnFlags::CONST)
595    }
596
597    pub fn is_async(&self) -> bool {
598        self.flags.contains(FnFlags::ASYNC)
599    }
600
601    pub fn is_unsafe(&self) -> bool {
602        self.flags.contains(FnFlags::UNSAFE)
603    }
604
605    pub fn is_deprecated_safe_2024(&self) -> bool {
606        self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
607    }
608
609    pub fn is_safe(&self) -> bool {
610        self.flags.contains(FnFlags::EXPLICIT_SAFE)
611    }
612
613    pub fn is_varargs(&self) -> bool {
614        self.flags.contains(FnFlags::HAS_VARARGS)
615    }
616
617    pub fn has_target_feature(&self) -> bool {
618        self.flags.contains(FnFlags::HAS_TARGET_FEATURE)
619    }
620}
621
622bitflags! {
623    #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
624    pub struct TypeAliasFlags: u8 {
625        const RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 1;
626        const IS_EXTERN = 1 << 6;
627        const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
628    }
629}
630
631#[derive(Debug, PartialEq, Eq)]
632pub struct TypeAliasSignature {
633    pub name: Name,
634    pub generic_params: Arc<GenericParams>,
635    pub store: Arc<ExpressionStore>,
636    pub bounds: Box<[TypeBound]>,
637    pub ty: Option<TypeRefId>,
638    pub flags: TypeAliasFlags,
639}
640
641impl TypeAliasSignature {
642    pub fn query(
643        db: &dyn DefDatabase,
644        id: TypeAliasId,
645    ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
646        let loc = id.lookup(db);
647
648        let mut flags = TypeAliasFlags::empty();
649        let attrs = db.attrs(id.into());
650        if attrs.by_key(sym::rustc_has_incoherent_inherent_impls).exists() {
651            flags.insert(TypeAliasFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPL);
652        }
653        if attrs.by_key(sym::rustc_allow_incoherent_impl).exists() {
654            flags.insert(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL);
655        }
656        if matches!(loc.container, ItemContainerId::ExternBlockId(_)) {
657            flags.insert(TypeAliasFlags::IS_EXTERN);
658        }
659        let source = loc.source(db);
660        let name = as_name_opt(source.value.name());
661        let (store, source_map, generic_params, bounds, ty) =
662            lower_type_alias(db, loc.container.module(db), source, id);
663
664        (
665            Arc::new(TypeAliasSignature {
666                store: Arc::new(store),
667                generic_params,
668                flags,
669                bounds,
670                name,
671                ty,
672            }),
673            Arc::new(source_map),
674        )
675    }
676}
677
678#[derive(Debug, PartialEq, Eq)]
679pub struct FunctionBody {
680    pub store: Arc<ExpressionStore>,
681    pub parameters: Box<[PatId]>,
682}
683
684#[derive(Debug, PartialEq, Eq)]
685pub struct SimpleBody {
686    pub store: Arc<ExpressionStore>,
687}
688pub type StaticBody = SimpleBody;
689pub type ConstBody = SimpleBody;
690pub type EnumVariantBody = SimpleBody;
691
692#[derive(Debug, PartialEq, Eq)]
693pub struct VariantFieldsBody {
694    pub store: Arc<ExpressionStore>,
695    pub fields: Box<[Option<ExprId>]>,
696}
697
698/// A single field of an enum variant or struct
699#[derive(Debug, Clone, PartialEq, Eq)]
700pub struct FieldData {
701    pub name: Name,
702    pub type_ref: TypeRefId,
703    pub visibility: RawVisibility,
704    pub is_unsafe: bool,
705}
706
707pub type LocalFieldId = Idx<FieldData>;
708
709#[derive(Debug, Clone, PartialEq, Eq)]
710pub struct VariantFields {
711    fields: Arena<FieldData>,
712    pub store: Arc<ExpressionStore>,
713    pub shape: FieldsShape,
714}
715
716#[salsa::tracked]
717impl VariantFields {
718    #[salsa::tracked(returns(clone))]
719    pub(crate) fn query(
720        db: &dyn DefDatabase,
721        id: VariantId,
722    ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) {
723        let (shape, result) = match id {
724            VariantId::EnumVariantId(id) => {
725                let loc = id.lookup(db);
726                let parent = loc.parent.lookup(db);
727                let source = loc.source(db);
728                let shape = adt_shape(source.value.kind());
729                let enum_vis = Some(source.value.parent_enum().visibility());
730                let fields = lower_field_list(
731                    db,
732                    parent.container,
733                    source.map(|src| src.field_list()),
734                    enum_vis,
735                );
736                (shape, fields)
737            }
738            VariantId::StructId(id) => {
739                let loc = id.lookup(db);
740                let source = loc.source(db);
741                let shape = adt_shape(source.value.kind());
742                let fields =
743                    lower_field_list(db, loc.container, source.map(|src| src.field_list()), None);
744                (shape, fields)
745            }
746            VariantId::UnionId(id) => {
747                let loc = id.lookup(db);
748                let source = loc.source(db);
749                let fields = lower_field_list(
750                    db,
751                    loc.container,
752                    source.map(|src| src.record_field_list().map(ast::FieldList::RecordFieldList)),
753                    None,
754                );
755                (FieldsShape::Record, fields)
756            }
757        };
758        match result {
759            Some((fields, store, source_map)) => (
760                Arc::new(VariantFields { fields, store: Arc::new(store), shape }),
761                Arc::new(source_map),
762            ),
763            None => {
764                let (store, source_map) = ExpressionStore::empty_singleton();
765                (Arc::new(VariantFields { fields: Arena::default(), store, shape }), source_map)
766            }
767        }
768    }
769
770    #[salsa::tracked(returns(deref))]
771    pub(crate) fn firewall(db: &dyn DefDatabase, id: VariantId) -> Arc<Self> {
772        Self::query(db, id).0
773    }
774}
775
776impl VariantFields {
777    pub fn len(&self) -> usize {
778        self.fields.len()
779    }
780
781    pub fn fields(&self) -> &Arena<FieldData> {
782        &self.fields
783    }
784
785    pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
786        self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
787    }
788}
789
790fn lower_field_list(
791    db: &dyn DefDatabase,
792    module: ModuleId,
793    fields: InFile<Option<ast::FieldList>>,
794    override_visibility: Option<Option<ast::Visibility>>,
795) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> {
796    let file_id = fields.file_id;
797    match fields.value? {
798        ast::FieldList::RecordFieldList(fields) => lower_fields(
799            db,
800            module,
801            InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
802            |_, field| as_name_opt(field.name()),
803            override_visibility,
804        ),
805        ast::FieldList::TupleFieldList(fields) => lower_fields(
806            db,
807            module,
808            InFile::new(file_id, fields.fields().map(|field| (field.ty(), field))),
809            |idx, _| Name::new_tuple_field(idx),
810            override_visibility,
811        ),
812    }
813}
814
815fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
816    db: &dyn DefDatabase,
817    module: ModuleId,
818    fields: InFile<impl Iterator<Item = (Option<ast::Type>, Field)>>,
819    mut field_name: impl FnMut(usize, &Field) -> Name,
820    override_visibility: Option<Option<ast::Visibility>>,
821) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> {
822    let cfg_options = module.krate.cfg_options(db);
823    let mut col = ExprCollector::new(db, module, fields.file_id);
824    let override_visibility = override_visibility.map(|vis| {
825        LazyCell::new(|| {
826            let span_map = db.span_map(fields.file_id);
827            visibility_from_ast(db, vis, &mut |range| span_map.span_for_range(range).ctx)
828        })
829    });
830
831    let mut arena = Arena::new();
832    let mut idx = 0;
833    let mut has_fields = false;
834    for (ty, field) in fields.value {
835        has_fields = true;
836        match Attrs::is_cfg_enabled_for(db, &field, col.span_map(), cfg_options) {
837            Ok(()) => {
838                let type_ref =
839                    col.lower_type_ref_opt(ty, &mut ExprCollector::impl_trait_error_allocator);
840                let visibility = override_visibility.as_ref().map_or_else(
841                    || {
842                        visibility_from_ast(db, field.visibility(), &mut |range| {
843                            col.span_map().span_for_range(range).ctx
844                        })
845                    },
846                    |it| RawVisibility::clone(it),
847                );
848                let is_unsafe = field
849                    .syntax()
850                    .children_with_tokens()
851                    .filter_map(NodeOrToken::into_token)
852                    .any(|token| token.kind() == T![unsafe]);
853                let name = field_name(idx, &field);
854                arena.alloc(FieldData { name, type_ref, visibility, is_unsafe });
855                idx += 1;
856            }
857            Err(cfg) => {
858                col.store.diagnostics.push(
859                    crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
860                        node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
861                        cfg,
862                        opts: cfg_options.clone(),
863                    },
864                );
865            }
866        }
867    }
868    if !has_fields {
869        return None;
870    }
871    let (store, source_map) = col.store.finish();
872    arena.shrink_to_fit();
873    Some((arena, store, source_map))
874}
875
876#[derive(Debug, PartialEq, Eq)]
877pub struct InactiveEnumVariantCode {
878    pub cfg: CfgExpr,
879    pub opts: CfgOptions,
880    pub ast_id: span::FileAstId<ast::Variant>,
881}
882
883#[derive(Debug, Clone, PartialEq, Eq)]
884pub struct EnumVariants {
885    pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>,
886}
887
888#[salsa::tracked]
889impl EnumVariants {
890    #[salsa::tracked(returns(ref))]
891    pub(crate) fn of(
892        db: &dyn DefDatabase,
893        e: EnumId,
894    ) -> (EnumVariants, Option<ThinVec<InactiveEnumVariantCode>>) {
895        let loc = e.lookup(db);
896        let source = loc.source(db);
897        let ast_id_map = db.ast_id_map(source.file_id);
898        let span_map = db.span_map(source.file_id);
899
900        let mut diagnostics = ThinVec::new();
901        let cfg_options = loc.container.krate.cfg_options(db);
902        let mut index = 0;
903        let Some(variants) = source.value.variant_list() else {
904            return (EnumVariants { variants: Box::default() }, None);
905        };
906        let variants = variants
907            .variants()
908            .filter_map(|variant| {
909                let ast_id = ast_id_map.ast_id(&variant);
910                match Attrs::is_cfg_enabled_for(db, &variant, span_map.as_ref(), cfg_options) {
911                    Ok(()) => {
912                        let enum_variant =
913                            EnumVariantLoc { id: source.with_value(ast_id), parent: e, index }
914                                .intern(db);
915                        index += 1;
916                        let name = as_name_opt(variant.name());
917                        let shape = adt_shape(variant.kind());
918                        Some((enum_variant, name, shape))
919                    }
920                    Err(cfg) => {
921                        diagnostics.push(InactiveEnumVariantCode {
922                            ast_id,
923                            cfg,
924                            opts: cfg_options.clone(),
925                        });
926                        None
927                    }
928                }
929            })
930            .collect();
931
932        (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics))
933    }
934}
935
936impl EnumVariants {
937    pub fn variant(&self, name: &Name) -> Option<EnumVariantId> {
938        self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None })
939    }
940
941    pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option<Name> {
942        self.variants
943            .iter()
944            .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None })
945    }
946
947    // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
948    pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
949        self.variants.iter().all(|&(v, _, _)| {
950            // The condition check order is slightly modified from rustc
951            // to improve performance by early returning with relatively fast checks
952            let variant = v.fields(db);
953            if !variant.fields().is_empty() {
954                return false;
955            }
956            // The outer if condition is whether this variant has const ctor or not
957            if !matches!(variant.shape, FieldsShape::Unit) {
958                let body = db.body(v.into());
959                // A variant with explicit discriminant
960                if !matches!(body[body.body_expr], crate::hir::Expr::Missing) {
961                    return false;
962                }
963            }
964            true
965        })
966    }
967}
968
969pub(crate) fn extern_block_abi(
970    db: &dyn DefDatabase,
971    extern_block: ExternBlockId,
972) -> Option<Symbol> {
973    let source = extern_block.lookup(db).source(db);
974    source.value.abi().map(|abi| {
975        match abi.abi_string() {
976            Some(tok) => Symbol::intern(tok.text_without_quotes()),
977            // `extern` default to be `extern "C"`.
978            _ => sym::C,
979        }
980    })
981}