1use std::ops::Bound;
2use std::{cmp, fmt};
3
4use rustc_abi::{
5 AddressSpace, Align, ExternAbi, FieldIdx, FieldsShape, HasDataLayout, LayoutData, PointeeInfo,
6 PointerKind, Primitive, ReprOptions, Scalar, Size, TagEncoding, TargetDataLayout,
7 TyAbiInterface, VariantIdx, Variants,
8};
9use rustc_error_messages::DiagMessage;
10use rustc_errors::{
11 Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
12};
13use rustc_hir::LangItem;
14use rustc_hir::def_id::DefId;
15use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension};
16use rustc_session::config::OptLevel;
17use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
18use rustc_target::callconv::FnAbi;
19use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, PanicStrategy, Target, X86Abi};
20use tracing::debug;
21use {rustc_abi as abi, rustc_hir as hir};
22
23use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
24use crate::query::TyCtxtAt;
25use crate::ty::normalize_erasing_regions::NormalizationError;
26use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt};
27
28#[extension(pub trait IntegerExt)]
29impl abi::Integer {
30 #[inline]
31 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> {
32 use abi::Integer::{I8, I16, I32, I64, I128};
33 match (*self, signed) {
34 (I8, false) => tcx.types.u8,
35 (I16, false) => tcx.types.u16,
36 (I32, false) => tcx.types.u32,
37 (I64, false) => tcx.types.u64,
38 (I128, false) => tcx.types.u128,
39 (I8, true) => tcx.types.i8,
40 (I16, true) => tcx.types.i16,
41 (I32, true) => tcx.types.i32,
42 (I64, true) => tcx.types.i64,
43 (I128, true) => tcx.types.i128,
44 }
45 }
46
47 fn from_int_ty<C: HasDataLayout>(cx: &C, ity: ty::IntTy) -> abi::Integer {
48 use abi::Integer::{I8, I16, I32, I64, I128};
49 match ity {
50 ty::IntTy::I8 => I8,
51 ty::IntTy::I16 => I16,
52 ty::IntTy::I32 => I32,
53 ty::IntTy::I64 => I64,
54 ty::IntTy::I128 => I128,
55 ty::IntTy::Isize => cx.data_layout().ptr_sized_integer(),
56 }
57 }
58 fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: ty::UintTy) -> abi::Integer {
59 use abi::Integer::{I8, I16, I32, I64, I128};
60 match ity {
61 ty::UintTy::U8 => I8,
62 ty::UintTy::U16 => I16,
63 ty::UintTy::U32 => I32,
64 ty::UintTy::U64 => I64,
65 ty::UintTy::U128 => I128,
66 ty::UintTy::Usize => cx.data_layout().ptr_sized_integer(),
67 }
68 }
69
70 fn repr_discr<'tcx>(
75 tcx: TyCtxt<'tcx>,
76 ty: Ty<'tcx>,
77 repr: &ReprOptions,
78 min: i128,
79 max: i128,
80 ) -> (abi::Integer, bool) {
81 let unsigned_fit = abi::Integer::fit_unsigned(cmp::max(min as u128, max as u128));
86 let signed_fit = cmp::max(abi::Integer::fit_signed(min), abi::Integer::fit_signed(max));
87
88 if let Some(ity) = repr.int {
89 let discr = abi::Integer::from_attr(&tcx, ity);
90 let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
91 if discr < fit {
92 bug!(
93 "Integer::repr_discr: `#[repr]` hint too small for \
94 discriminant range of enum `{}`",
95 ty
96 )
97 }
98 return (discr, ity.is_signed());
99 }
100
101 let at_least = if repr.c() {
102 tcx.data_layout().c_enum_min_size
105 } else {
106 abi::Integer::I8
108 };
109
110 if min >= 0 {
112 (cmp::max(unsigned_fit, at_least), false)
113 } else {
114 (cmp::max(signed_fit, at_least), true)
115 }
116 }
117}
118
119#[extension(pub trait FloatExt)]
120impl abi::Float {
121 #[inline]
122 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
123 use abi::Float::*;
124 match *self {
125 F16 => tcx.types.f16,
126 F32 => tcx.types.f32,
127 F64 => tcx.types.f64,
128 F128 => tcx.types.f128,
129 }
130 }
131
132 fn from_float_ty(fty: ty::FloatTy) -> Self {
133 use abi::Float::*;
134 match fty {
135 ty::FloatTy::F16 => F16,
136 ty::FloatTy::F32 => F32,
137 ty::FloatTy::F64 => F64,
138 ty::FloatTy::F128 => F128,
139 }
140 }
141}
142
143#[extension(pub trait PrimitiveExt)]
144impl Primitive {
145 #[inline]
146 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
147 match *self {
148 Primitive::Int(i, signed) => i.to_ty(tcx, signed),
149 Primitive::Float(f) => f.to_ty(tcx),
150 Primitive::Pointer(_) => Ty::new_mut_ptr(tcx, tcx.types.unit),
152 }
153 }
154
155 #[inline]
158 fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
159 match *self {
160 Primitive::Int(i, signed) => i.to_ty(tcx, signed),
161 Primitive::Pointer(_) => {
163 let signed = false;
164 tcx.data_layout().ptr_sized_integer().to_ty(tcx, signed)
165 }
166 Primitive::Float(_) => bug!("floats do not have an int type"),
167 }
168 }
169}
170
171pub const WIDE_PTR_ADDR: usize = 0;
176
177pub const WIDE_PTR_EXTRA: usize = 1;
182
183pub const MAX_SIMD_LANES: u64 = rustc_abi::MAX_SIMD_LANES;
184
185#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
188pub enum ValidityRequirement {
189 Inhabited,
190 Zero,
191 UninitMitigated0x01Fill,
194 Uninit,
196}
197
198impl ValidityRequirement {
199 pub fn from_intrinsic(intrinsic: Symbol) -> Option<Self> {
200 match intrinsic {
201 sym::assert_inhabited => Some(Self::Inhabited),
202 sym::assert_zero_valid => Some(Self::Zero),
203 sym::assert_mem_uninitialized_valid => Some(Self::UninitMitigated0x01Fill),
204 _ => None,
205 }
206 }
207}
208
209impl fmt::Display for ValidityRequirement {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 Self::Inhabited => f.write_str("is inhabited"),
213 Self::Zero => f.write_str("allows being left zeroed"),
214 Self::UninitMitigated0x01Fill => f.write_str("allows being filled with 0x01"),
215 Self::Uninit => f.write_str("allows being left uninitialized"),
216 }
217 }
218}
219
220#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
221pub enum LayoutError<'tcx> {
222 Unknown(Ty<'tcx>),
230 SizeOverflow(Ty<'tcx>),
232 TooGeneric(Ty<'tcx>),
237 NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>),
245 ReferencesError(ErrorGuaranteed),
247 Cycle(ErrorGuaranteed),
249}
250
251impl<'tcx> LayoutError<'tcx> {
252 pub fn diagnostic_message(&self) -> DiagMessage {
253 use LayoutError::*;
254
255 use crate::fluent_generated::*;
256 match self {
257 Unknown(_) => middle_layout_unknown,
258 SizeOverflow(_) => middle_layout_size_overflow,
259 TooGeneric(_) => middle_layout_too_generic,
260 NormalizationFailure(_, _) => middle_layout_normalization_failure,
261 Cycle(_) => middle_layout_cycle,
262 ReferencesError(_) => middle_layout_references_error,
263 }
264 }
265
266 pub fn into_diagnostic(self) -> crate::error::LayoutError<'tcx> {
267 use LayoutError::*;
268
269 use crate::error::LayoutError as E;
270 match self {
271 Unknown(ty) => E::Unknown { ty },
272 SizeOverflow(ty) => E::Overflow { ty },
273 TooGeneric(ty) => E::TooGeneric { ty },
274 NormalizationFailure(ty, e) => {
275 E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() }
276 }
277 Cycle(_) => E::Cycle,
278 ReferencesError(_) => E::ReferencesError,
279 }
280 }
281}
282
283impl<'tcx> fmt::Display for LayoutError<'tcx> {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 match *self {
288 LayoutError::Unknown(ty) => write!(f, "the type `{ty}` has an unknown layout"),
289 LayoutError::TooGeneric(ty) => {
290 write!(f, "the type `{ty}` does not have a fixed layout")
291 }
292 LayoutError::SizeOverflow(ty) => {
293 write!(f, "values of the type `{ty}` are too big for the target architecture")
294 }
295 LayoutError::NormalizationFailure(t, e) => write!(
296 f,
297 "unable to determine layout for `{}` because `{}` cannot be normalized",
298 t,
299 e.get_type_for_failure()
300 ),
301 LayoutError::Cycle(_) => write!(f, "a cycle occurred during layout computation"),
302 LayoutError::ReferencesError(_) => write!(f, "the type has an unknown layout"),
303 }
304 }
305}
306
307impl<'tcx> IntoDiagArg for LayoutError<'tcx> {
308 fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
309 self.to_string().into_diag_arg(&mut None)
310 }
311}
312
313#[derive(Clone, Copy)]
314pub struct LayoutCx<'tcx> {
315 pub calc: abi::LayoutCalculator<TyCtxt<'tcx>>,
316 pub typing_env: ty::TypingEnv<'tcx>,
317}
318
319impl<'tcx> LayoutCx<'tcx> {
320 pub fn new(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Self {
321 Self { calc: abi::LayoutCalculator::new(tcx), typing_env }
322 }
323}
324
325#[derive(Copy, Clone, Debug)]
330pub enum SizeSkeleton<'tcx> {
331 Known(Size, Option<Align>),
334
335 Generic(ty::Const<'tcx>),
340
341 Pointer {
343 non_zero: bool,
345 tail: Ty<'tcx>,
349 },
350}
351
352impl<'tcx> SizeSkeleton<'tcx> {
353 pub fn compute(
354 ty: Ty<'tcx>,
355 tcx: TyCtxt<'tcx>,
356 typing_env: ty::TypingEnv<'tcx>,
357 ) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
358 debug_assert!(!ty.has_non_region_infer());
359
360 let err = match tcx.layout_of(typing_env.as_query_input(ty)) {
362 Ok(layout) => {
363 if layout.is_sized() {
364 return Ok(SizeSkeleton::Known(layout.size, Some(layout.align.abi)));
365 } else {
366 return Err(tcx.arena.alloc(LayoutError::Unknown(ty)));
368 }
369 }
370 Err(err @ LayoutError::TooGeneric(_)) => err,
371 Err(
373 e @ LayoutError::Cycle(_)
374 | e @ LayoutError::Unknown(_)
375 | e @ LayoutError::SizeOverflow(_)
376 | e @ LayoutError::NormalizationFailure(..)
377 | e @ LayoutError::ReferencesError(_),
378 ) => return Err(e),
379 };
380
381 match *ty.kind() {
382 ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => {
383 let non_zero = !ty.is_raw_ptr();
384
385 let tail = tcx.struct_tail_raw(
386 pointee,
387 |ty| match tcx.try_normalize_erasing_regions(typing_env, ty) {
388 Ok(ty) => ty,
389 Err(e) => Ty::new_error_with_message(
390 tcx,
391 DUMMY_SP,
392 format!(
393 "normalization failed for {} but no errors reported",
394 e.get_type_for_failure()
395 ),
396 ),
397 },
398 || {},
399 );
400
401 match tail.kind() {
402 ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => {
403 debug_assert!(tail.has_non_region_param());
404 Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
405 }
406 ty::Error(guar) => {
407 return Err(tcx.arena.alloc(LayoutError::ReferencesError(*guar)));
409 }
410 _ => bug!(
411 "SizeSkeleton::compute({ty}): layout errored ({err:?}), yet \
412 tail `{tail}` is not a type parameter or a projection",
413 ),
414 }
415 }
416 ty::Array(inner, len) if tcx.features().transmute_generic_consts() => {
417 let len_eval = len.try_to_target_usize(tcx);
418 if len_eval == Some(0) {
419 return Ok(SizeSkeleton::Known(Size::from_bytes(0), None));
420 }
421
422 match SizeSkeleton::compute(inner, tcx, typing_env)? {
423 SizeSkeleton::Known(s, a) => {
426 if let Some(c) = len_eval {
427 let size = s
428 .bytes()
429 .checked_mul(c)
430 .ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?;
431 return Ok(SizeSkeleton::Known(Size::from_bytes(size), a));
433 }
434 Err(err)
435 }
436 SizeSkeleton::Pointer { .. } | SizeSkeleton::Generic(_) => Err(err),
437 }
438 }
439
440 ty::Adt(def, args) => {
441 if def.is_union() || def.variants().is_empty() || def.variants().len() > 2 {
443 return Err(err);
444 }
445
446 let zero_or_ptr_variant = |i| {
448 let i = VariantIdx::from_usize(i);
449 let fields =
450 def.variant(i).fields.iter().map(|field| {
451 SizeSkeleton::compute(field.ty(tcx, args), tcx, typing_env)
452 });
453 let mut ptr = None;
454 for field in fields {
455 let field = field?;
456 match field {
457 SizeSkeleton::Known(size, align) => {
458 let is_1zst = size.bytes() == 0
459 && align.is_some_and(|align| align.bytes() == 1);
460 if !is_1zst {
461 return Err(err);
462 }
463 }
464 SizeSkeleton::Pointer { .. } => {
465 if ptr.is_some() {
466 return Err(err);
467 }
468 ptr = Some(field);
469 }
470 SizeSkeleton::Generic(_) => {
471 return Err(err);
472 }
473 }
474 }
475 Ok(ptr)
476 };
477
478 let v0 = zero_or_ptr_variant(0)?;
479 if def.variants().len() == 1 {
481 if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
482 return Ok(SizeSkeleton::Pointer {
483 non_zero: non_zero
484 || match tcx.layout_scalar_valid_range(def.did()) {
485 (Bound::Included(start), Bound::Unbounded) => start > 0,
486 (Bound::Included(start), Bound::Included(end)) => {
487 0 < start && start < end
488 }
489 _ => false,
490 },
491 tail,
492 });
493 } else {
494 return Err(err);
495 }
496 }
497
498 let v1 = zero_or_ptr_variant(1)?;
499 match (v0, v1) {
501 (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None)
502 | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => {
503 Ok(SizeSkeleton::Pointer { non_zero: false, tail })
504 }
505 _ => Err(err),
506 }
507 }
508
509 ty::Alias(..) => {
510 let normalized = tcx.normalize_erasing_regions(typing_env, ty);
511 if ty == normalized {
512 Err(err)
513 } else {
514 SizeSkeleton::compute(normalized, tcx, typing_env)
515 }
516 }
517
518 ty::Pat(base, _) => SizeSkeleton::compute(base, tcx, typing_env),
520
521 _ => Err(err),
522 }
523 }
524
525 pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
526 match (self, other) {
527 (SizeSkeleton::Known(a, _), SizeSkeleton::Known(b, _)) => a == b,
528 (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
529 a == b
530 }
531 (SizeSkeleton::Generic(a), SizeSkeleton::Generic(b)) => a == b,
534 _ => false,
535 }
536 }
537}
538
539pub trait HasTyCtxt<'tcx>: HasDataLayout {
540 fn tcx(&self) -> TyCtxt<'tcx>;
541}
542
543pub trait HasTypingEnv<'tcx> {
544 fn typing_env(&self) -> ty::TypingEnv<'tcx>;
545
546 fn param_env(&self) -> ty::ParamEnv<'tcx> {
549 self.typing_env().param_env
550 }
551}
552
553impl<'tcx> HasDataLayout for TyCtxt<'tcx> {
554 #[inline]
555 fn data_layout(&self) -> &TargetDataLayout {
556 &self.data_layout
557 }
558}
559
560impl<'tcx> HasTargetSpec for TyCtxt<'tcx> {
561 fn target_spec(&self) -> &Target {
562 &self.sess.target
563 }
564}
565
566impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> {
567 fn x86_abi_opt(&self) -> X86Abi {
568 X86Abi {
569 regparm: self.sess.opts.unstable_opts.regparm,
570 reg_struct_return: self.sess.opts.unstable_opts.reg_struct_return,
571 }
572 }
573}
574
575impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> {
576 #[inline]
577 fn tcx(&self) -> TyCtxt<'tcx> {
578 *self
579 }
580}
581
582impl<'tcx> HasDataLayout for TyCtxtAt<'tcx> {
583 #[inline]
584 fn data_layout(&self) -> &TargetDataLayout {
585 &self.data_layout
586 }
587}
588
589impl<'tcx> HasTargetSpec for TyCtxtAt<'tcx> {
590 fn target_spec(&self) -> &Target {
591 &self.sess.target
592 }
593}
594
595impl<'tcx> HasTyCtxt<'tcx> for TyCtxtAt<'tcx> {
596 #[inline]
597 fn tcx(&self) -> TyCtxt<'tcx> {
598 **self
599 }
600}
601
602impl<'tcx> HasTypingEnv<'tcx> for LayoutCx<'tcx> {
603 fn typing_env(&self) -> ty::TypingEnv<'tcx> {
604 self.typing_env
605 }
606}
607
608impl<'tcx> HasDataLayout for LayoutCx<'tcx> {
609 fn data_layout(&self) -> &TargetDataLayout {
610 self.calc.cx.data_layout()
611 }
612}
613
614impl<'tcx> HasTargetSpec for LayoutCx<'tcx> {
615 fn target_spec(&self) -> &Target {
616 self.calc.cx.target_spec()
617 }
618}
619
620impl<'tcx> HasX86AbiOpt for LayoutCx<'tcx> {
621 fn x86_abi_opt(&self) -> X86Abi {
622 self.calc.cx.x86_abi_opt()
623 }
624}
625
626impl<'tcx> HasTyCtxt<'tcx> for LayoutCx<'tcx> {
627 fn tcx(&self) -> TyCtxt<'tcx> {
628 self.calc.cx
629 }
630}
631
632pub trait MaybeResult<T> {
633 type Error;
634
635 fn from(x: Result<T, Self::Error>) -> Self;
636 fn to_result(self) -> Result<T, Self::Error>;
637}
638
639impl<T> MaybeResult<T> for T {
640 type Error = !;
641
642 fn from(Ok(x): Result<T, Self::Error>) -> Self {
643 x
644 }
645 fn to_result(self) -> Result<T, Self::Error> {
646 Ok(self)
647 }
648}
649
650impl<T, E> MaybeResult<T> for Result<T, E> {
651 type Error = E;
652
653 fn from(x: Result<T, Self::Error>) -> Self {
654 x
655 }
656 fn to_result(self) -> Result<T, Self::Error> {
657 self
658 }
659}
660
661pub type TyAndLayout<'tcx> = rustc_abi::TyAndLayout<'tcx, Ty<'tcx>>;
662
663pub trait LayoutOfHelpers<'tcx>: HasDataLayout + HasTyCtxt<'tcx> + HasTypingEnv<'tcx> {
666 type LayoutOfResult: MaybeResult<TyAndLayout<'tcx>> = TyAndLayout<'tcx>;
669
670 #[inline]
673 fn layout_tcx_at_span(&self) -> Span {
674 DUMMY_SP
675 }
676
677 fn handle_layout_err(
685 &self,
686 err: LayoutError<'tcx>,
687 span: Span,
688 ty: Ty<'tcx>,
689 ) -> <Self::LayoutOfResult as MaybeResult<TyAndLayout<'tcx>>>::Error;
690}
691
692pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
694 #[inline]
697 fn layout_of(&self, ty: Ty<'tcx>) -> Self::LayoutOfResult {
698 self.spanned_layout_of(ty, DUMMY_SP)
699 }
700
701 #[inline]
706 fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::LayoutOfResult {
707 let span = if !span.is_dummy() { span } else { self.layout_tcx_at_span() };
708 let tcx = self.tcx().at(span);
709
710 MaybeResult::from(
711 tcx.layout_of(self.typing_env().as_query_input(ty))
712 .map_err(|err| self.handle_layout_err(*err, span, ty)),
713 )
714 }
715}
716
717impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}
718
719impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx> {
720 type LayoutOfResult = Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>>;
721
722 #[inline]
723 fn handle_layout_err(
724 &self,
725 err: LayoutError<'tcx>,
726 _: Span,
727 _: Ty<'tcx>,
728 ) -> &'tcx LayoutError<'tcx> {
729 self.tcx().arena.alloc(err)
730 }
731}
732
733impl<'tcx, C> TyAbiInterface<'tcx, C> for Ty<'tcx>
734where
735 C: HasTyCtxt<'tcx> + HasTypingEnv<'tcx>,
736{
737 fn ty_and_layout_for_variant(
738 this: TyAndLayout<'tcx>,
739 cx: &C,
740 variant_index: VariantIdx,
741 ) -> TyAndLayout<'tcx> {
742 let layout = match this.variants {
743 Variants::Single { index } if index == variant_index => {
745 return this;
746 }
747
748 Variants::Single { .. } | Variants::Empty => {
749 let tcx = cx.tcx();
754 let typing_env = cx.typing_env();
755
756 if let Ok(original_layout) = tcx.layout_of(typing_env.as_query_input(this.ty)) {
758 assert_eq!(original_layout.variants, this.variants);
759 }
760
761 let fields = match this.ty.kind() {
762 ty::Adt(def, _) if def.variants().is_empty() => {
763 bug!("for_variant called on zero-variant enum {}", this.ty)
764 }
765 ty::Adt(def, _) => def.variant(variant_index).fields.len(),
766 _ => bug!("`ty_and_layout_for_variant` on unexpected type {}", this.ty),
767 };
768 tcx.mk_layout(LayoutData::uninhabited_variant(cx, variant_index, fields))
769 }
770
771 Variants::Multiple { ref variants, .. } => {
772 cx.tcx().mk_layout(variants[variant_index].clone())
773 }
774 };
775
776 assert_eq!(*layout.variants(), Variants::Single { index: variant_index });
777
778 TyAndLayout { ty: this.ty, layout }
779 }
780
781 fn ty_and_layout_field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> TyAndLayout<'tcx> {
782 enum TyMaybeWithLayout<'tcx> {
783 Ty(Ty<'tcx>),
784 TyAndLayout(TyAndLayout<'tcx>),
785 }
786
787 fn field_ty_or_layout<'tcx>(
788 this: TyAndLayout<'tcx>,
789 cx: &(impl HasTyCtxt<'tcx> + HasTypingEnv<'tcx>),
790 i: usize,
791 ) -> TyMaybeWithLayout<'tcx> {
792 let tcx = cx.tcx();
793 let tag_layout = |tag: Scalar| -> TyAndLayout<'tcx> {
794 TyAndLayout {
795 layout: tcx.mk_layout(LayoutData::scalar(cx, tag)),
796 ty: tag.primitive().to_ty(tcx),
797 }
798 };
799
800 match *this.ty.kind() {
801 ty::Bool
802 | ty::Char
803 | ty::Int(_)
804 | ty::Uint(_)
805 | ty::Float(_)
806 | ty::FnPtr(..)
807 | ty::Never
808 | ty::FnDef(..)
809 | ty::CoroutineWitness(..)
810 | ty::Foreign(..)
811 | ty::Pat(_, _)
812 | ty::Dynamic(_, _, ty::Dyn) => {
813 bug!("TyAndLayout::field({:?}): not applicable", this)
814 }
815
816 ty::UnsafeBinder(bound_ty) => {
817 let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into());
818 field_ty_or_layout(TyAndLayout { ty, ..this }, cx, i)
819 }
820
821 ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => {
823 assert!(i < this.fields.count());
824
825 if i == 0 {
830 let nil = tcx.types.unit;
831 let unit_ptr_ty = if this.ty.is_raw_ptr() {
832 Ty::new_mut_ptr(tcx, nil)
833 } else {
834 Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, nil)
835 };
836
837 let typing_env = ty::TypingEnv::fully_monomorphized();
841 return TyMaybeWithLayout::TyAndLayout(TyAndLayout {
842 ty: this.ty,
843 ..tcx.layout_of(typing_env.as_query_input(unit_ptr_ty)).unwrap()
844 });
845 }
846
847 let mk_dyn_vtable = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
848 let min_count = ty::vtable_min_entries(
849 tcx,
850 principal.map(|principal| {
851 tcx.instantiate_bound_regions_with_erased(principal)
852 }),
853 );
854 Ty::new_imm_ref(
855 tcx,
856 tcx.lifetimes.re_static,
857 Ty::new_array(tcx, tcx.types.usize, min_count.try_into().unwrap()),
859 )
860 };
861
862 let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
863 && !pointee.references_error()
866 {
867 let metadata = tcx.normalize_erasing_regions(
868 cx.typing_env(),
869 Ty::new_projection(tcx, metadata_def_id, [pointee]),
870 );
871
872 if let ty::Adt(def, args) = metadata.kind()
877 && tcx.is_lang_item(def.did(), LangItem::DynMetadata)
878 && let ty::Dynamic(data, _, ty::Dyn) = args.type_at(0).kind()
879 {
880 mk_dyn_vtable(data.principal())
881 } else {
882 metadata
883 }
884 } else {
885 match tcx.struct_tail_for_codegen(pointee, cx.typing_env()).kind() {
886 ty::Slice(_) | ty::Str => tcx.types.usize,
887 ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()),
888 _ => bug!("TyAndLayout::field({:?}): not applicable", this),
889 }
890 };
891
892 TyMaybeWithLayout::Ty(metadata)
893 }
894
895 ty::Array(element, _) | ty::Slice(element) => TyMaybeWithLayout::Ty(element),
897 ty::Str => TyMaybeWithLayout::Ty(tcx.types.u8),
898
899 ty::Closure(_, args) => field_ty_or_layout(
901 TyAndLayout { ty: args.as_closure().tupled_upvars_ty(), ..this },
902 cx,
903 i,
904 ),
905
906 ty::CoroutineClosure(_, args) => field_ty_or_layout(
907 TyAndLayout { ty: args.as_coroutine_closure().tupled_upvars_ty(), ..this },
908 cx,
909 i,
910 ),
911
912 ty::Coroutine(def_id, args) => match this.variants {
913 Variants::Empty => unreachable!(),
914 Variants::Single { index } => TyMaybeWithLayout::Ty(
915 args.as_coroutine()
916 .state_tys(def_id, tcx)
917 .nth(index.as_usize())
918 .unwrap()
919 .nth(i)
920 .unwrap(),
921 ),
922 Variants::Multiple { tag, tag_field, .. } => {
923 if FieldIdx::from_usize(i) == tag_field {
924 return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
925 }
926 TyMaybeWithLayout::Ty(args.as_coroutine().prefix_tys()[i])
927 }
928 },
929
930 ty::Tuple(tys) => TyMaybeWithLayout::Ty(tys[i]),
931
932 ty::Adt(def, args) => {
934 match this.variants {
935 Variants::Single { index } => {
936 let field = &def.variant(index).fields[FieldIdx::from_usize(i)];
937 TyMaybeWithLayout::Ty(field.ty(tcx, args))
938 }
939 Variants::Empty => panic!("there is no field in Variants::Empty types"),
940
941 Variants::Multiple { tag, .. } => {
943 assert_eq!(i, 0);
944 return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
945 }
946 }
947 }
948
949 ty::Alias(..)
950 | ty::Bound(..)
951 | ty::Placeholder(..)
952 | ty::Param(_)
953 | ty::Infer(_)
954 | ty::Error(_) => bug!("TyAndLayout::field: unexpected type `{}`", this.ty),
955 }
956 }
957
958 match field_ty_or_layout(this, cx, i) {
959 TyMaybeWithLayout::Ty(field_ty) => {
960 cx.tcx().layout_of(cx.typing_env().as_query_input(field_ty)).unwrap_or_else(|e| {
961 bug!(
962 "failed to get layout for `{field_ty}`: {e:?},\n\
963 despite it being a field (#{i}) of an existing layout: {this:#?}",
964 )
965 })
966 }
967 TyMaybeWithLayout::TyAndLayout(field_layout) => field_layout,
968 }
969 }
970
971 fn ty_and_layout_pointee_info_at(
974 this: TyAndLayout<'tcx>,
975 cx: &C,
976 offset: Size,
977 ) -> Option<PointeeInfo> {
978 let tcx = cx.tcx();
979 let typing_env = cx.typing_env();
980
981 let pointee_info = match *this.ty.kind() {
982 ty::RawPtr(p_ty, _) if offset.bytes() == 0 => {
983 tcx.layout_of(typing_env.as_query_input(p_ty)).ok().map(|layout| PointeeInfo {
984 size: layout.size,
985 align: layout.align.abi,
986 safe: None,
987 })
988 }
989 ty::FnPtr(..) if offset.bytes() == 0 => {
990 tcx.layout_of(typing_env.as_query_input(this.ty)).ok().map(|layout| PointeeInfo {
991 size: layout.size,
992 align: layout.align.abi,
993 safe: None,
994 })
995 }
996 ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
997 let optimize = tcx.sess.opts.optimize != OptLevel::No;
1001 let kind = match mt {
1002 hir::Mutability::Not => {
1003 PointerKind::SharedRef { frozen: optimize && ty.is_freeze(tcx, typing_env) }
1004 }
1005 hir::Mutability::Mut => {
1006 PointerKind::MutableRef { unpin: optimize && ty.is_unpin(tcx, typing_env) }
1007 }
1008 };
1009
1010 tcx.layout_of(typing_env.as_query_input(ty)).ok().map(|layout| PointeeInfo {
1011 size: layout.size,
1012 align: layout.align.abi,
1013 safe: Some(kind),
1014 })
1015 }
1016
1017 _ => {
1018 let mut data_variant = match &this.variants {
1019 Variants::Multiple {
1029 tag_encoding:
1030 TagEncoding::Niche { untagged_variant, niche_variants, niche_start },
1031 tag_field,
1032 variants,
1033 ..
1034 } if variants.len() == 2
1035 && this.fields.offset(tag_field.as_usize()) == offset =>
1036 {
1037 let tagged_variant = if *untagged_variant == VariantIdx::ZERO {
1038 VariantIdx::from_u32(1)
1039 } else {
1040 VariantIdx::from_u32(0)
1041 };
1042 assert_eq!(tagged_variant, *niche_variants.start());
1043 if *niche_start == 0 {
1044 Some(this.for_variant(cx, *untagged_variant))
1050 } else {
1051 None
1052 }
1053 }
1054 Variants::Multiple { .. } => None,
1055 _ => Some(this),
1056 };
1057
1058 if let Some(variant) = data_variant {
1059 if let FieldsShape::Union(_) = variant.fields {
1061 data_variant = None;
1062 }
1063 }
1064
1065 let mut result = None;
1066
1067 if let Some(variant) = data_variant {
1068 let ptr_end = offset + Primitive::Pointer(AddressSpace::ZERO).size(cx);
1071 for i in 0..variant.fields.count() {
1072 let field_start = variant.fields.offset(i);
1073 if field_start <= offset {
1074 let field = variant.field(cx, i);
1075 result = field.to_result().ok().and_then(|field| {
1076 if ptr_end <= field_start + field.size {
1077 let field_info =
1079 field.pointee_info_at(cx, offset - field_start);
1080 field_info
1081 } else {
1082 None
1083 }
1084 });
1085 if result.is_some() {
1086 break;
1087 }
1088 }
1089 }
1090 }
1091
1092 if let Some(ref mut pointee) = result {
1096 if offset.bytes() == 0
1097 && let Some(boxed_ty) = this.ty.boxed_ty()
1098 {
1099 debug_assert!(pointee.safe.is_none());
1100 let optimize = tcx.sess.opts.optimize != OptLevel::No;
1101 pointee.safe = Some(PointerKind::Box {
1102 unpin: optimize && boxed_ty.is_unpin(tcx, typing_env),
1103 global: this.ty.is_box_global(tcx),
1104 });
1105 }
1106 }
1107
1108 result
1109 }
1110 };
1111
1112 debug!(
1113 "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
1114 offset,
1115 this.ty.kind(),
1116 pointee_info
1117 );
1118
1119 pointee_info
1120 }
1121
1122 fn is_adt(this: TyAndLayout<'tcx>) -> bool {
1123 matches!(this.ty.kind(), ty::Adt(..))
1124 }
1125
1126 fn is_never(this: TyAndLayout<'tcx>) -> bool {
1127 matches!(this.ty.kind(), ty::Never)
1128 }
1129
1130 fn is_tuple(this: TyAndLayout<'tcx>) -> bool {
1131 matches!(this.ty.kind(), ty::Tuple(..))
1132 }
1133
1134 fn is_unit(this: TyAndLayout<'tcx>) -> bool {
1135 matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
1136 }
1137
1138 fn is_transparent(this: TyAndLayout<'tcx>) -> bool {
1139 matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent())
1140 }
1141}
1142
1143#[inline]
1184#[tracing::instrument(level = "debug", skip(tcx))]
1185pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) -> bool {
1186 if let Some(did) = fn_def_id {
1187 if tcx.codegen_fn_attrs(did).flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) {
1189 return false;
1190 }
1191
1192 if tcx.sess.panic_strategy() == PanicStrategy::Abort && !tcx.is_foreign_item(did) {
1197 return false;
1198 }
1199
1200 if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort
1205 && tcx.is_lang_item(did, LangItem::DropInPlace)
1206 {
1207 return false;
1208 }
1209 }
1210
1211 use ExternAbi::*;
1218 match abi {
1219 C { unwind }
1220 | System { unwind }
1221 | Cdecl { unwind }
1222 | Stdcall { unwind }
1223 | Fastcall { unwind }
1224 | Vectorcall { unwind }
1225 | Thiscall { unwind }
1226 | Aapcs { unwind }
1227 | Win64 { unwind }
1228 | SysV64 { unwind } => unwind,
1229 PtxKernel
1230 | Msp430Interrupt
1231 | X86Interrupt
1232 | GpuKernel
1233 | EfiApi
1234 | AvrInterrupt
1235 | AvrNonBlockingInterrupt
1236 | CmseNonSecureCall
1237 | CmseNonSecureEntry
1238 | Custom
1239 | RiscvInterruptM
1240 | RiscvInterruptS
1241 | RustInvalid
1242 | Unadjusted => false,
1243 Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
1244 }
1245}
1246
1247#[derive(Copy, Clone, Debug, HashStable)]
1249pub enum FnAbiError<'tcx> {
1250 Layout(LayoutError<'tcx>),
1252}
1253
1254impl<'a, 'b, G: EmissionGuarantee> Diagnostic<'a, G> for FnAbiError<'b> {
1255 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
1256 match self {
1257 Self::Layout(e) => e.into_diagnostic().into_diag(dcx, level),
1258 }
1259 }
1260}
1261
1262#[derive(Debug)]
1265pub enum FnAbiRequest<'tcx> {
1266 OfFnPtr { sig: ty::PolyFnSig<'tcx>, extra_args: &'tcx ty::List<Ty<'tcx>> },
1267 OfInstance { instance: ty::Instance<'tcx>, extra_args: &'tcx ty::List<Ty<'tcx>> },
1268}
1269
1270pub trait FnAbiOfHelpers<'tcx>: LayoutOfHelpers<'tcx> {
1273 type FnAbiOfResult: MaybeResult<&'tcx FnAbi<'tcx, Ty<'tcx>>> = &'tcx FnAbi<'tcx, Ty<'tcx>>;
1276
1277 fn handle_fn_abi_err(
1285 &self,
1286 err: FnAbiError<'tcx>,
1287 span: Span,
1288 fn_abi_request: FnAbiRequest<'tcx>,
1289 ) -> <Self::FnAbiOfResult as MaybeResult<&'tcx FnAbi<'tcx, Ty<'tcx>>>>::Error;
1290}
1291
1292pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
1294 #[inline]
1299 fn fn_abi_of_fn_ptr(
1300 &self,
1301 sig: ty::PolyFnSig<'tcx>,
1302 extra_args: &'tcx ty::List<Ty<'tcx>>,
1303 ) -> Self::FnAbiOfResult {
1304 let span = self.layout_tcx_at_span();
1306 let tcx = self.tcx().at(span);
1307
1308 MaybeResult::from(
1309 tcx.fn_abi_of_fn_ptr(self.typing_env().as_query_input((sig, extra_args))).map_err(
1310 |err| self.handle_fn_abi_err(*err, span, FnAbiRequest::OfFnPtr { sig, extra_args }),
1311 ),
1312 )
1313 }
1314
1315 #[inline]
1321 #[tracing::instrument(level = "debug", skip(self))]
1322 fn fn_abi_of_instance(
1323 &self,
1324 instance: ty::Instance<'tcx>,
1325 extra_args: &'tcx ty::List<Ty<'tcx>>,
1326 ) -> Self::FnAbiOfResult {
1327 let span = self.layout_tcx_at_span();
1329 let tcx = self.tcx().at(span);
1330
1331 MaybeResult::from(
1332 tcx.fn_abi_of_instance(self.typing_env().as_query_input((instance, extra_args)))
1333 .map_err(|err| {
1334 let span =
1339 if !span.is_dummy() { span } else { tcx.def_span(instance.def_id()) };
1340 self.handle_fn_abi_err(
1341 *err,
1342 span,
1343 FnAbiRequest::OfInstance { instance, extra_args },
1344 )
1345 }),
1346 )
1347 }
1348}
1349
1350impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {}
1351
1352impl<'tcx> TyCtxt<'tcx> {
1353 pub fn offset_of_subfield<I>(
1354 self,
1355 typing_env: ty::TypingEnv<'tcx>,
1356 mut layout: TyAndLayout<'tcx>,
1357 indices: I,
1358 ) -> Size
1359 where
1360 I: Iterator<Item = (VariantIdx, FieldIdx)>,
1361 {
1362 let cx = LayoutCx::new(self, typing_env);
1363 let mut offset = Size::ZERO;
1364
1365 for (variant, field) in indices {
1366 layout = layout.for_variant(&cx, variant);
1367 let index = field.index();
1368 offset += layout.fields.offset(index);
1369 layout = layout.field(&cx, index);
1370 if !layout.is_sized() {
1371 let tail = self.struct_tail_for_codegen(layout.ty, typing_env);
1373 if !matches!(tail.kind(), ty::Slice(..)) {
1374 bug!(
1375 "offset of not-statically-aligned field (type {:?}) cannot be computed statically",
1376 layout.ty
1377 );
1378 }
1379 }
1380 }
1381
1382 offset
1383 }
1384}