1use std::cell::RefCell;
2use std::hash::{Hash, Hasher};
3use std::ops::Range;
4use std::str;
5
6use rustc_abi::{FIRST_VARIANT, ReprOptions, VariantIdx};
7use rustc_data_structures::fingerprint::Fingerprint;
8use rustc_data_structures::fx::FxHashMap;
9use rustc_data_structures::intern::Interned;
10use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher};
11use rustc_errors::ErrorGuaranteed;
12use rustc_hir::def::{CtorKind, DefKind, Res};
13use rustc_hir::def_id::DefId;
14use rustc_hir::{self as hir, LangItem};
15use rustc_index::{IndexSlice, IndexVec};
16use rustc_macros::{HashStable, TyDecodable, TyEncodable};
17use rustc_query_system::ich::StableHashingContext;
18use rustc_session::DataTypeKind;
19use rustc_span::sym;
20use rustc_type_ir::solve::AdtDestructorKind;
21use tracing::{debug, info, trace};
22
23use super::{
24 AsyncDestructor, Destructor, FieldDef, GenericPredicates, Ty, TyCtxt, VariantDef, VariantDiscr,
25};
26use crate::mir::interpret::ErrorHandled;
27use crate::ty;
28use crate::ty::util::{Discr, IntTypeExt};
29
30#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
31pub struct AdtFlags(u16);
32bitflags::bitflags! {
33 impl AdtFlags: u16 {
34 const NO_ADT_FLAGS = 0;
35 const IS_ENUM = 1 << 0;
37 const IS_UNION = 1 << 1;
39 const IS_STRUCT = 1 << 2;
41 const HAS_CTOR = 1 << 3;
43 const IS_PHANTOM_DATA = 1 << 4;
45 const IS_FUNDAMENTAL = 1 << 5;
47 const IS_BOX = 1 << 6;
49 const IS_MANUALLY_DROP = 1 << 7;
51 const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8;
54 const IS_UNSAFE_CELL = 1 << 9;
56 }
57}
58rustc_data_structures::external_bitflags_debug! { AdtFlags }
59
60#[derive(TyEncodable, TyDecodable)]
94pub struct AdtDefData {
95 pub did: DefId,
97 variants: IndexVec<VariantIdx, VariantDef>,
99 flags: AdtFlags,
101 repr: ReprOptions,
103}
104
105impl PartialEq for AdtDefData {
106 #[inline]
107 fn eq(&self, other: &Self) -> bool {
108 let Self { did: self_def_id, variants: _, flags: _, repr: _ } = self;
116 let Self { did: other_def_id, variants: _, flags: _, repr: _ } = other;
117
118 let res = self_def_id == other_def_id;
119
120 if cfg!(debug_assertions) && res {
122 let deep = self.flags == other.flags
123 && self.repr == other.repr
124 && self.variants == other.variants;
125 assert!(deep, "AdtDefData for the same def-id has differing data");
126 }
127
128 res
129 }
130}
131
132impl Eq for AdtDefData {}
133
134impl Hash for AdtDefData {
137 #[inline]
138 fn hash<H: Hasher>(&self, s: &mut H) {
139 self.did.hash(s)
140 }
141}
142
143impl<'a> HashStable<StableHashingContext<'a>> for AdtDefData {
144 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
145 thread_local! {
146 static CACHE: RefCell<FxHashMap<(usize, HashingControls), Fingerprint>> = Default::default();
147 }
148
149 let hash: Fingerprint = CACHE.with(|cache| {
150 let addr = self as *const AdtDefData as usize;
151 let hashing_controls = hcx.hashing_controls();
152 *cache.borrow_mut().entry((addr, hashing_controls)).or_insert_with(|| {
153 let ty::AdtDefData { did, ref variants, ref flags, ref repr } = *self;
154
155 let mut hasher = StableHasher::new();
156 did.hash_stable(hcx, &mut hasher);
157 variants.hash_stable(hcx, &mut hasher);
158 flags.hash_stable(hcx, &mut hasher);
159 repr.hash_stable(hcx, &mut hasher);
160
161 hasher.finish()
162 })
163 });
164
165 hash.hash_stable(hcx, hasher);
166 }
167}
168
169#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)]
170#[rustc_pass_by_value]
171pub struct AdtDef<'tcx>(pub Interned<'tcx, AdtDefData>);
172
173impl<'tcx> AdtDef<'tcx> {
174 #[inline]
175 pub fn did(self) -> DefId {
176 self.0.0.did
177 }
178
179 #[inline]
180 pub fn variants(self) -> &'tcx IndexSlice<VariantIdx, VariantDef> {
181 &self.0.0.variants
182 }
183
184 #[inline]
185 pub fn variant(self, idx: VariantIdx) -> &'tcx VariantDef {
186 &self.0.0.variants[idx]
187 }
188
189 #[inline]
190 pub fn flags(self) -> AdtFlags {
191 self.0.0.flags
192 }
193
194 #[inline]
195 pub fn repr(self) -> ReprOptions {
196 self.0.0.repr
197 }
198}
199
200impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
201 fn def_id(self) -> DefId {
202 self.did()
203 }
204
205 fn is_struct(self) -> bool {
206 self.is_struct()
207 }
208
209 fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
210 Some(interner.type_of(self.non_enum_variant().tail_opt()?.did))
211 }
212
213 fn is_phantom_data(self) -> bool {
214 self.is_phantom_data()
215 }
216
217 fn is_manually_drop(self) -> bool {
218 self.is_manually_drop()
219 }
220
221 fn all_field_tys(
222 self,
223 tcx: TyCtxt<'tcx>,
224 ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = Ty<'tcx>>> {
225 ty::EarlyBinder::bind(
226 self.all_fields().map(move |field| tcx.type_of(field.did).skip_binder()),
227 )
228 }
229
230 fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
231 self.sized_constraint(tcx)
232 }
233
234 fn is_fundamental(self) -> bool {
235 self.is_fundamental()
236 }
237
238 fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<AdtDestructorKind> {
239 Some(match self.destructor(tcx)?.constness {
240 hir::Constness::Const => AdtDestructorKind::Const,
241 hir::Constness::NotConst => AdtDestructorKind::NotConst,
242 })
243 }
244}
245
246#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)]
247pub enum AdtKind {
248 Struct,
249 Union,
250 Enum,
251}
252
253impl From<AdtKind> for DataTypeKind {
254 fn from(val: AdtKind) -> Self {
255 match val {
256 AdtKind::Struct => DataTypeKind::Struct,
257 AdtKind::Union => DataTypeKind::Union,
258 AdtKind::Enum => DataTypeKind::Enum,
259 }
260 }
261}
262
263impl AdtDefData {
264 pub(super) fn new(
266 tcx: TyCtxt<'_>,
267 did: DefId,
268 kind: AdtKind,
269 variants: IndexVec<VariantIdx, VariantDef>,
270 repr: ReprOptions,
271 ) -> Self {
272 debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr);
273 let mut flags = AdtFlags::NO_ADT_FLAGS;
274
275 if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) {
276 debug!("found non-exhaustive variant list for {:?}", did);
277 flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
278 }
279
280 flags |= match kind {
281 AdtKind::Enum => AdtFlags::IS_ENUM,
282 AdtKind::Union => AdtFlags::IS_UNION,
283 AdtKind::Struct => AdtFlags::IS_STRUCT,
284 };
285
286 if kind == AdtKind::Struct && variants[FIRST_VARIANT].ctor.is_some() {
287 flags |= AdtFlags::HAS_CTOR;
288 }
289
290 if tcx.has_attr(did, sym::fundamental) {
291 flags |= AdtFlags::IS_FUNDAMENTAL;
292 }
293 if tcx.is_lang_item(did, LangItem::PhantomData) {
294 flags |= AdtFlags::IS_PHANTOM_DATA;
295 }
296 if tcx.is_lang_item(did, LangItem::OwnedBox) {
297 flags |= AdtFlags::IS_BOX;
298 }
299 if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
300 flags |= AdtFlags::IS_MANUALLY_DROP;
301 }
302 if tcx.is_lang_item(did, LangItem::UnsafeCell) {
303 flags |= AdtFlags::IS_UNSAFE_CELL;
304 }
305
306 AdtDefData { did, variants, flags, repr }
307 }
308}
309
310impl<'tcx> AdtDef<'tcx> {
311 #[inline]
313 pub fn is_struct(self) -> bool {
314 self.flags().contains(AdtFlags::IS_STRUCT)
315 }
316
317 #[inline]
319 pub fn is_union(self) -> bool {
320 self.flags().contains(AdtFlags::IS_UNION)
321 }
322
323 #[inline]
325 pub fn is_enum(self) -> bool {
326 self.flags().contains(AdtFlags::IS_ENUM)
327 }
328
329 #[inline]
335 pub fn is_variant_list_non_exhaustive(self) -> bool {
336 self.flags().contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE)
337 }
338
339 #[inline]
342 pub fn variant_list_has_applicable_non_exhaustive(self) -> bool {
343 self.is_variant_list_non_exhaustive() && !self.did().is_local()
344 }
345
346 #[inline]
348 pub fn adt_kind(self) -> AdtKind {
349 if self.is_enum() {
350 AdtKind::Enum
351 } else if self.is_union() {
352 AdtKind::Union
353 } else {
354 AdtKind::Struct
355 }
356 }
357
358 pub fn descr(self) -> &'static str {
360 match self.adt_kind() {
361 AdtKind::Struct => "struct",
362 AdtKind::Union => "union",
363 AdtKind::Enum => "enum",
364 }
365 }
366
367 #[inline]
369 pub fn variant_descr(self) -> &'static str {
370 match self.adt_kind() {
371 AdtKind::Struct => "struct",
372 AdtKind::Union => "union",
373 AdtKind::Enum => "variant",
374 }
375 }
376
377 #[inline]
379 pub fn has_ctor(self) -> bool {
380 self.flags().contains(AdtFlags::HAS_CTOR)
381 }
382
383 #[inline]
386 pub fn is_fundamental(self) -> bool {
387 self.flags().contains(AdtFlags::IS_FUNDAMENTAL)
388 }
389
390 #[inline]
392 pub fn is_phantom_data(self) -> bool {
393 self.flags().contains(AdtFlags::IS_PHANTOM_DATA)
394 }
395
396 #[inline]
398 pub fn is_box(self) -> bool {
399 self.flags().contains(AdtFlags::IS_BOX)
400 }
401
402 #[inline]
404 pub fn is_unsafe_cell(self) -> bool {
405 self.flags().contains(AdtFlags::IS_UNSAFE_CELL)
406 }
407
408 #[inline]
410 pub fn is_manually_drop(self) -> bool {
411 self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
412 }
413
414 pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
416 self.destructor(tcx).is_some()
417 }
418
419 pub fn non_enum_variant(self) -> &'tcx VariantDef {
421 assert!(self.is_struct() || self.is_union());
422 self.variant(FIRST_VARIANT)
423 }
424
425 #[inline]
426 pub fn predicates(self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> {
427 tcx.predicates_of(self.did())
428 }
429
430 #[inline]
433 pub fn all_fields(self) -> impl Iterator<Item = &'tcx FieldDef> + Clone {
434 self.variants().iter().flat_map(|v| v.fields.iter())
435 }
436
437 pub fn is_payloadfree(self) -> bool {
440 if self.variants().iter().any(|v| {
450 matches!(v.discr, VariantDiscr::Explicit(_)) && v.ctor_kind() != Some(CtorKind::Const)
451 }) {
452 return false;
453 }
454 self.variants().iter().all(|v| v.fields.is_empty())
455 }
456
457 pub fn variant_with_id(self, vid: DefId) -> &'tcx VariantDef {
459 self.variants().iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant")
460 }
461
462 pub fn variant_with_ctor_id(self, cid: DefId) -> &'tcx VariantDef {
464 self.variants()
465 .iter()
466 .find(|v| v.ctor_def_id() == Some(cid))
467 .expect("variant_with_ctor_id: unknown variant")
468 }
469
470 #[inline]
472 pub fn variant_index_with_id(self, vid: DefId) -> VariantIdx {
473 self.variants()
474 .iter_enumerated()
475 .find(|(_, v)| v.def_id == vid)
476 .expect("variant_index_with_id: unknown variant")
477 .0
478 }
479
480 pub fn variant_index_with_ctor_id(self, cid: DefId) -> VariantIdx {
482 self.variants()
483 .iter_enumerated()
484 .find(|(_, v)| v.ctor_def_id() == Some(cid))
485 .expect("variant_index_with_ctor_id: unknown variant")
486 .0
487 }
488
489 pub fn variant_of_res(self, res: Res) -> &'tcx VariantDef {
490 match res {
491 Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid),
492 Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid),
493 Res::Def(DefKind::Struct, _)
494 | Res::Def(DefKind::Union, _)
495 | Res::Def(DefKind::TyAlias, _)
496 | Res::Def(DefKind::AssocTy, _)
497 | Res::SelfTyParam { .. }
498 | Res::SelfTyAlias { .. }
499 | Res::SelfCtor(..) => self.non_enum_variant(),
500 _ => bug!("unexpected res {:?} in variant_of_res", res),
501 }
502 }
503
504 #[inline]
505 pub fn eval_explicit_discr(
506 self,
507 tcx: TyCtxt<'tcx>,
508 expr_did: DefId,
509 ) -> Result<Discr<'tcx>, ErrorGuaranteed> {
510 assert!(self.is_enum());
511
512 let repr_type = self.repr().discr_type();
513 match tcx.const_eval_poly(expr_did) {
514 Ok(val) => {
515 let typing_env = ty::TypingEnv::post_analysis(tcx, expr_did);
516 let ty = repr_type.to_ty(tcx);
517 if let Some(b) = val.try_to_bits_for_ty(tcx, typing_env, ty) {
518 trace!("discriminants: {} ({:?})", b, repr_type);
519 Ok(Discr { val: b, ty })
520 } else {
521 info!("invalid enum discriminant: {:#?}", val);
522 let guar = tcx.dcx().emit_err(crate::error::ConstEvalNonIntError {
523 span: tcx.def_span(expr_did),
524 });
525 Err(guar)
526 }
527 }
528 Err(err) => {
529 let guar = match err {
530 ErrorHandled::Reported(info, _) => info.into(),
531 ErrorHandled::TooGeneric(..) => tcx.dcx().span_delayed_bug(
532 tcx.def_span(expr_did),
533 "enum discriminant depends on generics",
534 ),
535 };
536 Err(guar)
537 }
538 }
539 }
540
541 #[inline]
542 pub fn discriminants(
543 self,
544 tcx: TyCtxt<'tcx>,
545 ) -> impl Iterator<Item = (VariantIdx, Discr<'tcx>)> {
546 assert!(self.is_enum());
547 let repr_type = self.repr().discr_type();
548 let initial = repr_type.initial_discriminant(tcx);
549 let mut prev_discr = None::<Discr<'tcx>>;
550 self.variants().iter_enumerated().map(move |(i, v)| {
551 let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx));
552 if let VariantDiscr::Explicit(expr_did) = v.discr {
553 if let Ok(new_discr) = self.eval_explicit_discr(tcx, expr_did) {
554 discr = new_discr;
555 }
556 }
557 prev_discr = Some(discr);
558
559 (i, discr)
560 })
561 }
562
563 #[inline]
564 pub fn variant_range(self) -> Range<VariantIdx> {
565 FIRST_VARIANT..self.variants().next_index()
566 }
567
568 #[inline]
574 pub fn discriminant_for_variant(
575 self,
576 tcx: TyCtxt<'tcx>,
577 variant_index: VariantIdx,
578 ) -> Discr<'tcx> {
579 assert!(self.is_enum());
580 let (val, offset) = self.discriminant_def_for_variant(variant_index);
581 let explicit_value = if let Some(expr_did) = val
582 && let Ok(val) = self.eval_explicit_discr(tcx, expr_did)
583 {
584 val
585 } else {
586 self.repr().discr_type().initial_discriminant(tcx)
587 };
588 explicit_value.checked_add(tcx, offset as u128).0
589 }
590
591 pub fn discriminant_def_for_variant(self, variant_index: VariantIdx) -> (Option<DefId>, u32) {
595 assert!(!self.variants().is_empty());
596 let mut explicit_index = variant_index.as_u32();
597 let expr_did;
598 loop {
599 match self.variant(VariantIdx::from_u32(explicit_index)).discr {
600 ty::VariantDiscr::Relative(0) => {
601 expr_did = None;
602 break;
603 }
604 ty::VariantDiscr::Relative(distance) => {
605 explicit_index -= distance;
606 }
607 ty::VariantDiscr::Explicit(did) => {
608 expr_did = Some(did);
609 break;
610 }
611 }
612 }
613 (expr_did, variant_index.as_u32() - explicit_index)
614 }
615
616 pub fn destructor(self, tcx: TyCtxt<'tcx>) -> Option<Destructor> {
617 tcx.adt_destructor(self.did())
618 }
619
620 pub fn async_destructor(self, tcx: TyCtxt<'tcx>) -> Option<AsyncDestructor> {
623 tcx.adt_async_destructor(self.did())
624 }
625
626 pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
629 if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None }
630 }
631}
632
633#[derive(Clone, Copy, Debug, HashStable)]
634pub enum Representability {
635 Representable,
636 Infinite(ErrorGuaranteed),
637}