Skip to main content

hir_def/
signatures.rs

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