]> git.proxmox.com Git - rustc.git/blame_incremental - compiler/rustc_middle/src/ty/layout.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / layout.rs
... / ...
CommitLineData
1use crate::fluent_generated as fluent;
2use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
3use crate::ty::normalize_erasing_regions::NormalizationError;
4use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
5use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic};
6use rustc_hir as hir;
7use rustc_hir::def_id::DefId;
8use rustc_index::vec::Idx;
9use rustc_session::config::OptLevel;
10use rustc_span::symbol::{sym, Symbol};
11use rustc_span::{Span, DUMMY_SP};
12use rustc_target::abi::call::FnAbi;
13use rustc_target::abi::*;
14use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
15
16use std::cmp::{self};
17use std::fmt;
18use std::num::NonZeroUsize;
19use std::ops::Bound;
20
21pub trait IntegerExt {
22 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>;
23 fn from_int_ty<C: HasDataLayout>(cx: &C, ity: ty::IntTy) -> Integer;
24 fn from_uint_ty<C: HasDataLayout>(cx: &C, uty: ty::UintTy) -> Integer;
25 fn repr_discr<'tcx>(
26 tcx: TyCtxt<'tcx>,
27 ty: Ty<'tcx>,
28 repr: &ReprOptions,
29 min: i128,
30 max: i128,
31 ) -> (Integer, bool);
32}
33
34impl IntegerExt for Integer {
35 #[inline]
36 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> {
37 match (*self, signed) {
38 (I8, false) => tcx.types.u8,
39 (I16, false) => tcx.types.u16,
40 (I32, false) => tcx.types.u32,
41 (I64, false) => tcx.types.u64,
42 (I128, false) => tcx.types.u128,
43 (I8, true) => tcx.types.i8,
44 (I16, true) => tcx.types.i16,
45 (I32, true) => tcx.types.i32,
46 (I64, true) => tcx.types.i64,
47 (I128, true) => tcx.types.i128,
48 }
49 }
50
51 fn from_int_ty<C: HasDataLayout>(cx: &C, ity: ty::IntTy) -> Integer {
52 match ity {
53 ty::IntTy::I8 => I8,
54 ty::IntTy::I16 => I16,
55 ty::IntTy::I32 => I32,
56 ty::IntTy::I64 => I64,
57 ty::IntTy::I128 => I128,
58 ty::IntTy::Isize => cx.data_layout().ptr_sized_integer(),
59 }
60 }
61 fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: ty::UintTy) -> Integer {
62 match ity {
63 ty::UintTy::U8 => I8,
64 ty::UintTy::U16 => I16,
65 ty::UintTy::U32 => I32,
66 ty::UintTy::U64 => I64,
67 ty::UintTy::U128 => I128,
68 ty::UintTy::Usize => cx.data_layout().ptr_sized_integer(),
69 }
70 }
71
72 /// Finds the appropriate Integer type and signedness for the given
73 /// signed discriminant range and `#[repr]` attribute.
74 /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
75 /// that shouldn't affect anything, other than maybe debuginfo.
76 fn repr_discr<'tcx>(
77 tcx: TyCtxt<'tcx>,
78 ty: Ty<'tcx>,
79 repr: &ReprOptions,
80 min: i128,
81 max: i128,
82 ) -> (Integer, bool) {
83 // Theoretically, negative values could be larger in unsigned representation
84 // than the unsigned representation of the signed minimum. However, if there
85 // are any negative values, the only valid unsigned representation is u128
86 // which can fit all i128 values, so the result remains unaffected.
87 let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
88 let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
89
90 if let Some(ity) = repr.int {
91 let discr = Integer::from_attr(&tcx, ity);
92 let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
93 if discr < fit {
94 bug!(
95 "Integer::repr_discr: `#[repr]` hint too small for \
96 discriminant range of enum `{}`",
97 ty
98 )
99 }
100 return (discr, ity.is_signed());
101 }
102
103 let at_least = if repr.c() {
104 // This is usually I32, however it can be different on some platforms,
105 // notably hexagon and arm-none/thumb-none
106 tcx.data_layout().c_enum_min_size
107 } else {
108 // repr(Rust) enums try to be as small as possible
109 I8
110 };
111
112 // If there are no negative values, we can use the unsigned fit.
113 if min >= 0 {
114 (cmp::max(unsigned_fit, at_least), false)
115 } else {
116 (cmp::max(signed_fit, at_least), true)
117 }
118 }
119}
120
121pub trait PrimitiveExt {
122 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
123 fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
124}
125
126impl PrimitiveExt for Primitive {
127 #[inline]
128 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
129 match *self {
130 Int(i, signed) => i.to_ty(tcx, signed),
131 F32 => tcx.types.f32,
132 F64 => tcx.types.f64,
133 // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
134 Pointer(_) => tcx.mk_mut_ptr(tcx.mk_unit()),
135 }
136 }
137
138 /// Return an *integer* type matching this primitive.
139 /// Useful in particular when dealing with enum discriminants.
140 #[inline]
141 fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
142 match *self {
143 Int(i, signed) => i.to_ty(tcx, signed),
144 // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
145 Pointer(_) => {
146 let signed = false;
147 tcx.data_layout().ptr_sized_integer().to_ty(tcx, signed)
148 }
149 F32 | F64 => bug!("floats do not have an int type"),
150 }
151 }
152}
153
154/// The first half of a fat pointer.
155///
156/// - For a trait object, this is the address of the box.
157/// - For a slice, this is the base address.
158pub const FAT_PTR_ADDR: usize = 0;
159
160/// The second half of a fat pointer.
161///
162/// - For a trait object, this is the address of the vtable.
163/// - For a slice, this is the length.
164pub const FAT_PTR_EXTRA: usize = 1;
165
166/// The maximum supported number of lanes in a SIMD vector.
167///
168/// This value is selected based on backend support:
169/// * LLVM does not appear to have a vector width limit.
170/// * Cranelift stores the base-2 log of the lane count in a 4 bit integer.
171pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
172
173/// Used in `check_validity_requirement` to indicate the kind of initialization
174/// that is checked to be valid
175#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
176pub enum ValidityRequirement {
177 Inhabited,
178 Zero,
179 /// The return value of mem::uninitialized, 0x01
180 /// (unless -Zstrict-init-checks is on, in which case it's the same as Uninit).
181 UninitMitigated0x01Fill,
182 /// True uninitialized memory.
183 Uninit,
184}
185
186impl ValidityRequirement {
187 pub fn from_intrinsic(intrinsic: Symbol) -> Option<Self> {
188 match intrinsic {
189 sym::assert_inhabited => Some(Self::Inhabited),
190 sym::assert_zero_valid => Some(Self::Zero),
191 sym::assert_mem_uninitialized_valid => Some(Self::UninitMitigated0x01Fill),
192 _ => None,
193 }
194 }
195}
196
197impl fmt::Display for ValidityRequirement {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 match self {
200 Self::Inhabited => f.write_str("is inhabited"),
201 Self::Zero => f.write_str("allows being left zeroed"),
202 Self::UninitMitigated0x01Fill => f.write_str("allows being filled with 0x01"),
203 Self::Uninit => f.write_str("allows being left uninitialized"),
204 }
205 }
206}
207
208#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
209pub enum LayoutError<'tcx> {
210 Unknown(Ty<'tcx>),
211 SizeOverflow(Ty<'tcx>),
212 NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>),
213}
214
215impl IntoDiagnostic<'_, !> for LayoutError<'_> {
216 fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
217 let mut diag = handler.struct_fatal("");
218
219 match self {
220 LayoutError::Unknown(ty) => {
221 diag.set_arg("ty", ty);
222 diag.set_primary_message(fluent::middle_unknown_layout);
223 }
224 LayoutError::SizeOverflow(ty) => {
225 diag.set_arg("ty", ty);
226 diag.set_primary_message(fluent::middle_values_too_big);
227 }
228 LayoutError::NormalizationFailure(ty, e) => {
229 diag.set_arg("ty", ty);
230 diag.set_arg("failure_ty", e.get_type_for_failure());
231 diag.set_primary_message(fluent::middle_cannot_be_normalized);
232 }
233 }
234 diag
235 }
236}
237
238// FIXME: Once the other errors that embed this error have been converted to translateable
239// diagnostics, this Display impl should be removed.
240impl<'tcx> fmt::Display for LayoutError<'tcx> {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 match *self {
243 LayoutError::Unknown(ty) => write!(f, "the type `{}` has an unknown layout", ty),
244 LayoutError::SizeOverflow(ty) => {
245 write!(f, "values of the type `{}` are too big for the current architecture", ty)
246 }
247 LayoutError::NormalizationFailure(t, e) => write!(
248 f,
249 "unable to determine layout for `{}` because `{}` cannot be normalized",
250 t,
251 e.get_type_for_failure()
252 ),
253 }
254 }
255}
256
257#[derive(Clone, Copy)]
258pub struct LayoutCx<'tcx, C> {
259 pub tcx: C,
260 pub param_env: ty::ParamEnv<'tcx>,
261}
262
263impl<'tcx> LayoutCalculator for LayoutCx<'tcx, TyCtxt<'tcx>> {
264 type TargetDataLayoutRef = &'tcx TargetDataLayout;
265
266 fn delay_bug(&self, txt: &str) {
267 self.tcx.sess.delay_span_bug(DUMMY_SP, txt);
268 }
269
270 fn current_data_layout(&self) -> Self::TargetDataLayoutRef {
271 &self.tcx.data_layout
272 }
273}
274
275/// Type size "skeleton", i.e., the only information determining a type's size.
276/// While this is conservative, (aside from constant sizes, only pointers,
277/// newtypes thereof and null pointer optimized enums are allowed), it is
278/// enough to statically check common use cases of transmute.
279#[derive(Copy, Clone, Debug)]
280pub enum SizeSkeleton<'tcx> {
281 /// Any statically computable Layout.
282 Known(Size),
283
284 /// A potentially-fat pointer.
285 Pointer {
286 /// If true, this pointer is never null.
287 non_zero: bool,
288 /// The type which determines the unsized metadata, if any,
289 /// of this pointer. Either a type parameter or a projection
290 /// depending on one, with regions erased.
291 tail: Ty<'tcx>,
292 },
293}
294
295impl<'tcx> SizeSkeleton<'tcx> {
296 pub fn compute(
297 ty: Ty<'tcx>,
298 tcx: TyCtxt<'tcx>,
299 param_env: ty::ParamEnv<'tcx>,
300 ) -> Result<SizeSkeleton<'tcx>, LayoutError<'tcx>> {
301 debug_assert!(!ty.has_non_region_infer());
302
303 // First try computing a static layout.
304 let err = match tcx.layout_of(param_env.and(ty)) {
305 Ok(layout) => {
306 return Ok(SizeSkeleton::Known(layout.size));
307 }
308 Err(err) => err,
309 };
310
311 match *ty.kind() {
312 ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
313 let non_zero = !ty.is_unsafe_ptr();
314 let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
315 match tail.kind() {
316 ty::Param(_) | ty::Alias(ty::Projection, _) => {
317 debug_assert!(tail.has_non_region_param());
318 Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
319 }
320 _ => bug!(
321 "SizeSkeleton::compute({}): layout errored ({}), yet \
322 tail `{}` is not a type parameter or a projection",
323 ty,
324 err,
325 tail
326 ),
327 }
328 }
329
330 ty::Adt(def, substs) => {
331 // Only newtypes and enums w/ nullable pointer optimization.
332 if def.is_union() || def.variants().is_empty() || def.variants().len() > 2 {
333 return Err(err);
334 }
335
336 // Get a zero-sized variant or a pointer newtype.
337 let zero_or_ptr_variant = |i| {
338 let i = VariantIdx::new(i);
339 let fields =
340 def.variant(i).fields.iter().map(|field| {
341 SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)
342 });
343 let mut ptr = None;
344 for field in fields {
345 let field = field?;
346 match field {
347 SizeSkeleton::Known(size) => {
348 if size.bytes() > 0 {
349 return Err(err);
350 }
351 }
352 SizeSkeleton::Pointer { .. } => {
353 if ptr.is_some() {
354 return Err(err);
355 }
356 ptr = Some(field);
357 }
358 }
359 }
360 Ok(ptr)
361 };
362
363 let v0 = zero_or_ptr_variant(0)?;
364 // Newtype.
365 if def.variants().len() == 1 {
366 if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
367 return Ok(SizeSkeleton::Pointer {
368 non_zero: non_zero
369 || match tcx.layout_scalar_valid_range(def.did()) {
370 (Bound::Included(start), Bound::Unbounded) => start > 0,
371 (Bound::Included(start), Bound::Included(end)) => {
372 0 < start && start < end
373 }
374 _ => false,
375 },
376 tail,
377 });
378 } else {
379 return Err(err);
380 }
381 }
382
383 let v1 = zero_or_ptr_variant(1)?;
384 // Nullable pointer enum optimization.
385 match (v0, v1) {
386 (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None)
387 | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => {
388 Ok(SizeSkeleton::Pointer { non_zero: false, tail })
389 }
390 _ => Err(err),
391 }
392 }
393
394 ty::Alias(..) => {
395 let normalized = tcx.normalize_erasing_regions(param_env, ty);
396 if ty == normalized {
397 Err(err)
398 } else {
399 SizeSkeleton::compute(normalized, tcx, param_env)
400 }
401 }
402
403 _ => Err(err),
404 }
405 }
406
407 pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
408 match (self, other) {
409 (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b,
410 (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
411 a == b
412 }
413 _ => false,
414 }
415 }
416}
417
418pub trait HasTyCtxt<'tcx>: HasDataLayout {
419 fn tcx(&self) -> TyCtxt<'tcx>;
420}
421
422pub trait HasParamEnv<'tcx> {
423 fn param_env(&self) -> ty::ParamEnv<'tcx>;
424}
425
426impl<'tcx> HasDataLayout for TyCtxt<'tcx> {
427 #[inline]
428 fn data_layout(&self) -> &TargetDataLayout {
429 &self.data_layout
430 }
431}
432
433impl<'tcx> HasTargetSpec for TyCtxt<'tcx> {
434 fn target_spec(&self) -> &Target {
435 &self.sess.target
436 }
437}
438
439impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> {
440 #[inline]
441 fn tcx(&self) -> TyCtxt<'tcx> {
442 *self
443 }
444}
445
446impl<'tcx> HasDataLayout for ty::query::TyCtxtAt<'tcx> {
447 #[inline]
448 fn data_layout(&self) -> &TargetDataLayout {
449 &self.data_layout
450 }
451}
452
453impl<'tcx> HasTargetSpec for ty::query::TyCtxtAt<'tcx> {
454 fn target_spec(&self) -> &Target {
455 &self.sess.target
456 }
457}
458
459impl<'tcx> HasTyCtxt<'tcx> for ty::query::TyCtxtAt<'tcx> {
460 #[inline]
461 fn tcx(&self) -> TyCtxt<'tcx> {
462 **self
463 }
464}
465
466impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> {
467 fn param_env(&self) -> ty::ParamEnv<'tcx> {
468 self.param_env
469 }
470}
471
472impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> {
473 fn data_layout(&self) -> &TargetDataLayout {
474 self.tcx.data_layout()
475 }
476}
477
478impl<'tcx, T: HasTargetSpec> HasTargetSpec for LayoutCx<'tcx, T> {
479 fn target_spec(&self) -> &Target {
480 self.tcx.target_spec()
481 }
482}
483
484impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
485 fn tcx(&self) -> TyCtxt<'tcx> {
486 self.tcx.tcx()
487 }
488}
489
490pub trait MaybeResult<T> {
491 type Error;
492
493 fn from(x: Result<T, Self::Error>) -> Self;
494 fn to_result(self) -> Result<T, Self::Error>;
495}
496
497impl<T> MaybeResult<T> for T {
498 type Error = !;
499
500 fn from(Ok(x): Result<T, Self::Error>) -> Self {
501 x
502 }
503 fn to_result(self) -> Result<T, Self::Error> {
504 Ok(self)
505 }
506}
507
508impl<T, E> MaybeResult<T> for Result<T, E> {
509 type Error = E;
510
511 fn from(x: Result<T, Self::Error>) -> Self {
512 x
513 }
514 fn to_result(self) -> Result<T, Self::Error> {
515 self
516 }
517}
518
519pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>;
520
521/// Trait for contexts that want to be able to compute layouts of types.
522/// This automatically gives access to `LayoutOf`, through a blanket `impl`.
523pub trait LayoutOfHelpers<'tcx>: HasDataLayout + HasTyCtxt<'tcx> + HasParamEnv<'tcx> {
524 /// The `TyAndLayout`-wrapping type (or `TyAndLayout` itself), which will be
525 /// returned from `layout_of` (see also `handle_layout_err`).
526 type LayoutOfResult: MaybeResult<TyAndLayout<'tcx>>;
527
528 /// `Span` to use for `tcx.at(span)`, from `layout_of`.
529 // FIXME(eddyb) perhaps make this mandatory to get contexts to track it better?
530 #[inline]
531 fn layout_tcx_at_span(&self) -> Span {
532 DUMMY_SP
533 }
534
535 /// Helper used for `layout_of`, to adapt `tcx.layout_of(...)` into a
536 /// `Self::LayoutOfResult` (which does not need to be a `Result<...>`).
537 ///
538 /// Most `impl`s, which propagate `LayoutError`s, should simply return `err`,
539 /// but this hook allows e.g. codegen to return only `TyAndLayout` from its
540 /// `cx.layout_of(...)`, without any `Result<...>` around it to deal with
541 /// (and any `LayoutError`s are turned into fatal errors or ICEs).
542 fn handle_layout_err(
543 &self,
544 err: LayoutError<'tcx>,
545 span: Span,
546 ty: Ty<'tcx>,
547 ) -> <Self::LayoutOfResult as MaybeResult<TyAndLayout<'tcx>>>::Error;
548}
549
550/// Blanket extension trait for contexts that can compute layouts of types.
551pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
552 /// Computes the layout of a type. Note that this implicitly
553 /// executes in "reveal all" mode, and will normalize the input type.
554 #[inline]
555 fn layout_of(&self, ty: Ty<'tcx>) -> Self::LayoutOfResult {
556 self.spanned_layout_of(ty, DUMMY_SP)
557 }
558
559 /// Computes the layout of a type, at `span`. Note that this implicitly
560 /// executes in "reveal all" mode, and will normalize the input type.
561 // FIXME(eddyb) avoid passing information like this, and instead add more
562 // `TyCtxt::at`-like APIs to be able to do e.g. `cx.at(span).layout_of(ty)`.
563 #[inline]
564 fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::LayoutOfResult {
565 let span = if !span.is_dummy() { span } else { self.layout_tcx_at_span() };
566 let tcx = self.tcx().at(span);
567
568 MaybeResult::from(
569 tcx.layout_of(self.param_env().and(ty))
570 .map_err(|err| self.handle_layout_err(err, span, ty)),
571 )
572 }
573}
574
575impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}
576
577impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxt<'tcx>> {
578 type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
579
580 #[inline]
581 fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
582 err
583 }
584}
585
586impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> {
587 type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
588
589 #[inline]
590 fn layout_tcx_at_span(&self) -> Span {
591 self.tcx.span
592 }
593
594 #[inline]
595 fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
596 err
597 }
598}
599
600impl<'tcx, C> TyAbiInterface<'tcx, C> for Ty<'tcx>
601where
602 C: HasTyCtxt<'tcx> + HasParamEnv<'tcx>,
603{
604 fn ty_and_layout_for_variant(
605 this: TyAndLayout<'tcx>,
606 cx: &C,
607 variant_index: VariantIdx,
608 ) -> TyAndLayout<'tcx> {
609 let layout = match this.variants {
610 Variants::Single { index }
611 // If all variants but one are uninhabited, the variant layout is the enum layout.
612 if index == variant_index &&
613 // Don't confuse variants of uninhabited enums with the enum itself.
614 // For more details see https://github.com/rust-lang/rust/issues/69763.
615 this.fields != FieldsShape::Primitive =>
616 {
617 this.layout
618 }
619
620 Variants::Single { index } => {
621 let tcx = cx.tcx();
622 let param_env = cx.param_env();
623
624 // Deny calling for_variant more than once for non-Single enums.
625 if let Ok(original_layout) = tcx.layout_of(param_env.and(this.ty)) {
626 assert_eq!(original_layout.variants, Variants::Single { index });
627 }
628
629 let fields = match this.ty.kind() {
630 ty::Adt(def, _) if def.variants().is_empty() =>
631 bug!("for_variant called on zero-variant enum"),
632 ty::Adt(def, _) => def.variant(variant_index).fields.len(),
633 _ => bug!(),
634 };
635 tcx.mk_layout(LayoutS {
636 variants: Variants::Single { index: variant_index },
637 fields: match NonZeroUsize::new(fields) {
638 Some(fields) => FieldsShape::Union(fields),
639 None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] },
640 },
641 abi: Abi::Uninhabited,
642 largest_niche: None,
643 align: tcx.data_layout.i8_align,
644 size: Size::ZERO,
645 })
646 }
647
648 Variants::Multiple { ref variants, .. } => cx.tcx().mk_layout(variants[variant_index].clone()),
649 };
650
651 assert_eq!(*layout.variants(), Variants::Single { index: variant_index });
652
653 TyAndLayout { ty: this.ty, layout }
654 }
655
656 fn ty_and_layout_field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> TyAndLayout<'tcx> {
657 enum TyMaybeWithLayout<'tcx> {
658 Ty(Ty<'tcx>),
659 TyAndLayout(TyAndLayout<'tcx>),
660 }
661
662 fn field_ty_or_layout<'tcx>(
663 this: TyAndLayout<'tcx>,
664 cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>),
665 i: usize,
666 ) -> TyMaybeWithLayout<'tcx> {
667 let tcx = cx.tcx();
668 let tag_layout = |tag: Scalar| -> TyAndLayout<'tcx> {
669 TyAndLayout {
670 layout: tcx.mk_layout(LayoutS::scalar(cx, tag)),
671 ty: tag.primitive().to_ty(tcx),
672 }
673 };
674
675 match *this.ty.kind() {
676 ty::Bool
677 | ty::Char
678 | ty::Int(_)
679 | ty::Uint(_)
680 | ty::Float(_)
681 | ty::FnPtr(_)
682 | ty::Never
683 | ty::FnDef(..)
684 | ty::GeneratorWitness(..)
685 | ty::GeneratorWitnessMIR(..)
686 | ty::Foreign(..)
687 | ty::Dynamic(_, _, ty::Dyn) => {
688 bug!("TyAndLayout::field({:?}): not applicable", this)
689 }
690
691 // Potentially-fat pointers.
692 ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
693 assert!(i < this.fields.count());
694
695 // Reuse the fat `*T` type as its own thin pointer data field.
696 // This provides information about, e.g., DST struct pointees
697 // (which may have no non-DST form), and will work as long
698 // as the `Abi` or `FieldsShape` is checked by users.
699 if i == 0 {
700 let nil = tcx.mk_unit();
701 let unit_ptr_ty = if this.ty.is_unsafe_ptr() {
702 tcx.mk_mut_ptr(nil)
703 } else {
704 tcx.mk_mut_ref(tcx.lifetimes.re_static, nil)
705 };
706
707 // NOTE(eddyb) using an empty `ParamEnv`, and `unwrap`-ing
708 // the `Result` should always work because the type is
709 // always either `*mut ()` or `&'static mut ()`.
710 return TyMaybeWithLayout::TyAndLayout(TyAndLayout {
711 ty: this.ty,
712 ..tcx.layout_of(ty::ParamEnv::reveal_all().and(unit_ptr_ty)).unwrap()
713 });
714 }
715
716 let mk_dyn_vtable = || {
717 tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3))
718 /* FIXME: use actual fn pointers
719 Warning: naively computing the number of entries in the
720 vtable by counting the methods on the trait + methods on
721 all parent traits does not work, because some methods can
722 be not object safe and thus excluded from the vtable.
723 Increase this counter if you tried to implement this but
724 failed to do it without duplicating a lot of code from
725 other places in the compiler: 2
726 tcx.mk_tup(&[
727 tcx.mk_array(tcx.types.usize, 3),
728 tcx.mk_array(Option<fn()>),
729 ])
730 */
731 };
732
733 let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() {
734 let metadata = tcx.normalize_erasing_regions(
735 cx.param_env(),
736 tcx.mk_projection(metadata_def_id, [pointee]),
737 );
738
739 // Map `Metadata = DynMetadata<dyn Trait>` back to a vtable, since it
740 // offers better information than `std::ptr::metadata::VTable`,
741 // and we rely on this layout information to trigger a panic in
742 // `std::mem::uninitialized::<&dyn Trait>()`, for example.
743 if let ty::Adt(def, substs) = metadata.kind()
744 && Some(def.did()) == tcx.lang_items().dyn_metadata()
745 && substs.type_at(0).is_trait()
746 {
747 mk_dyn_vtable()
748 } else {
749 metadata
750 }
751 } else {
752 match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
753 ty::Slice(_) | ty::Str => tcx.types.usize,
754 ty::Dynamic(_, _, ty::Dyn) => mk_dyn_vtable(),
755 _ => bug!("TyAndLayout::field({:?}): not applicable", this),
756 }
757 };
758
759 TyMaybeWithLayout::Ty(metadata)
760 }
761
762 // Arrays and slices.
763 ty::Array(element, _) | ty::Slice(element) => TyMaybeWithLayout::Ty(element),
764 ty::Str => TyMaybeWithLayout::Ty(tcx.types.u8),
765
766 // Tuples, generators and closures.
767 ty::Closure(_, ref substs) => field_ty_or_layout(
768 TyAndLayout { ty: substs.as_closure().tupled_upvars_ty(), ..this },
769 cx,
770 i,
771 ),
772
773 ty::Generator(def_id, ref substs, _) => match this.variants {
774 Variants::Single { index } => TyMaybeWithLayout::Ty(
775 substs
776 .as_generator()
777 .state_tys(def_id, tcx)
778 .nth(index.as_usize())
779 .unwrap()
780 .nth(i)
781 .unwrap(),
782 ),
783 Variants::Multiple { tag, tag_field, .. } => {
784 if i == tag_field {
785 return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
786 }
787 TyMaybeWithLayout::Ty(substs.as_generator().prefix_tys().nth(i).unwrap())
788 }
789 },
790
791 ty::Tuple(tys) => TyMaybeWithLayout::Ty(tys[i]),
792
793 // ADTs.
794 ty::Adt(def, substs) => {
795 match this.variants {
796 Variants::Single { index } => {
797 TyMaybeWithLayout::Ty(def.variant(index).fields[i].ty(tcx, substs))
798 }
799
800 // Discriminant field for enums (where applicable).
801 Variants::Multiple { tag, .. } => {
802 assert_eq!(i, 0);
803 return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
804 }
805 }
806 }
807
808 ty::Dynamic(_, _, ty::DynStar) => {
809 if i == 0 {
810 TyMaybeWithLayout::Ty(tcx.mk_mut_ptr(tcx.types.unit))
811 } else if i == 1 {
812 // FIXME(dyn-star) same FIXME as above applies here too
813 TyMaybeWithLayout::Ty(
814 tcx.mk_imm_ref(
815 tcx.lifetimes.re_static,
816 tcx.mk_array(tcx.types.usize, 3),
817 ),
818 )
819 } else {
820 bug!("no field {i} on dyn*")
821 }
822 }
823
824 ty::Alias(..)
825 | ty::Bound(..)
826 | ty::Placeholder(..)
827 | ty::Param(_)
828 | ty::Infer(_)
829 | ty::Error(_) => bug!("TyAndLayout::field: unexpected type `{}`", this.ty),
830 }
831 }
832
833 match field_ty_or_layout(this, cx, i) {
834 TyMaybeWithLayout::Ty(field_ty) => {
835 cx.tcx().layout_of(cx.param_env().and(field_ty)).unwrap_or_else(|e| {
836 bug!(
837 "failed to get layout for `{}`: {},\n\
838 despite it being a field (#{}) of an existing layout: {:#?}",
839 field_ty,
840 e,
841 i,
842 this
843 )
844 })
845 }
846 TyMaybeWithLayout::TyAndLayout(field_layout) => field_layout,
847 }
848 }
849
850 fn ty_and_layout_pointee_info_at(
851 this: TyAndLayout<'tcx>,
852 cx: &C,
853 offset: Size,
854 ) -> Option<PointeeInfo> {
855 let tcx = cx.tcx();
856 let param_env = cx.param_env();
857
858 let pointee_info = match *this.ty.kind() {
859 ty::RawPtr(mt) if offset.bytes() == 0 => {
860 tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
861 size: layout.size,
862 align: layout.align.abi,
863 safe: None,
864 })
865 }
866 ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
867 tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
868 size: layout.size,
869 align: layout.align.abi,
870 safe: None,
871 })
872 }
873 ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
874 // Use conservative pointer kind if not optimizing. This saves us the
875 // Freeze/Unpin queries, and can save time in the codegen backend (noalias
876 // attributes in LLVM have compile-time cost even in unoptimized builds).
877 let optimize = tcx.sess.opts.optimize != OptLevel::No;
878 let kind = match mt {
879 hir::Mutability::Not => PointerKind::SharedRef {
880 frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
881 },
882 hir::Mutability::Mut => PointerKind::MutableRef {
883 unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
884 },
885 };
886
887 tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
888 size: layout.size,
889 align: layout.align.abi,
890 safe: Some(kind),
891 })
892 }
893
894 _ => {
895 let mut data_variant = match this.variants {
896 // Within the discriminant field, only the niche itself is
897 // always initialized, so we only check for a pointer at its
898 // offset.
899 //
900 // If the niche is a pointer, it's either valid (according
901 // to its type), or null (which the niche field's scalar
902 // validity range encodes). This allows using
903 // `dereferenceable_or_null` for e.g., `Option<&T>`, and
904 // this will continue to work as long as we don't start
905 // using more niches than just null (e.g., the first page of
906 // the address space, or unaligned pointers).
907 Variants::Multiple {
908 tag_encoding: TagEncoding::Niche { untagged_variant, .. },
909 tag_field,
910 ..
911 } if this.fields.offset(tag_field) == offset => {
912 Some(this.for_variant(cx, untagged_variant))
913 }
914 _ => Some(this),
915 };
916
917 if let Some(variant) = data_variant {
918 // We're not interested in any unions.
919 if let FieldsShape::Union(_) = variant.fields {
920 data_variant = None;
921 }
922 }
923
924 let mut result = None;
925
926 if let Some(variant) = data_variant {
927 // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
928 // (requires passing in the expected address space from the caller)
929 let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
930 for i in 0..variant.fields.count() {
931 let field_start = variant.fields.offset(i);
932 if field_start <= offset {
933 let field = variant.field(cx, i);
934 result = field.to_result().ok().and_then(|field| {
935 if ptr_end <= field_start + field.size {
936 // We found the right field, look inside it.
937 let field_info =
938 field.pointee_info_at(cx, offset - field_start);
939 field_info
940 } else {
941 None
942 }
943 });
944 if result.is_some() {
945 break;
946 }
947 }
948 }
949 }
950
951 // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
952 if let Some(ref mut pointee) = result {
953 if let ty::Adt(def, _) = this.ty.kind() {
954 if def.is_box() && offset.bytes() == 0 {
955 let optimize = tcx.sess.opts.optimize != OptLevel::No;
956 pointee.safe = Some(PointerKind::Box {
957 unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
958 });
959 }
960 }
961 }
962
963 result
964 }
965 };
966
967 debug!(
968 "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
969 offset,
970 this.ty.kind(),
971 pointee_info
972 );
973
974 pointee_info
975 }
976
977 fn is_adt(this: TyAndLayout<'tcx>) -> bool {
978 matches!(this.ty.kind(), ty::Adt(..))
979 }
980
981 fn is_never(this: TyAndLayout<'tcx>) -> bool {
982 this.ty.kind() == &ty::Never
983 }
984
985 fn is_tuple(this: TyAndLayout<'tcx>) -> bool {
986 matches!(this.ty.kind(), ty::Tuple(..))
987 }
988
989 fn is_unit(this: TyAndLayout<'tcx>) -> bool {
990 matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
991 }
992}
993
994/// Calculates whether a function's ABI can unwind or not.
995///
996/// This takes two primary parameters:
997///
998/// * `codegen_fn_attr_flags` - these are flags calculated as part of the
999/// codegen attrs for a defined function. For function pointers this set of
1000/// flags is the empty set. This is only applicable for Rust-defined
1001/// functions, and generally isn't needed except for small optimizations where
1002/// we try to say a function which otherwise might look like it could unwind
1003/// doesn't actually unwind (such as for intrinsics and such).
1004///
1005/// * `abi` - this is the ABI that the function is defined with. This is the
1006/// primary factor for determining whether a function can unwind or not.
1007///
1008/// Note that in this case unwinding is not necessarily panicking in Rust. Rust
1009/// panics are implemented with unwinds on most platform (when
1010/// `-Cpanic=unwind`), but this also accounts for `-Cpanic=abort` build modes.
1011/// Notably unwinding is disallowed for more non-Rust ABIs unless it's
1012/// specifically in the name (e.g. `"C-unwind"`). Unwinding within each ABI is
1013/// defined for each ABI individually, but it always corresponds to some form of
1014/// stack-based unwinding (the exact mechanism of which varies
1015/// platform-by-platform).
1016///
1017/// Rust functions are classified whether or not they can unwind based on the
1018/// active "panic strategy". In other words Rust functions are considered to
1019/// unwind in `-Cpanic=unwind` mode and cannot unwind in `-Cpanic=abort` mode.
1020/// Note that Rust supports intermingling panic=abort and panic=unwind code, but
1021/// only if the final panic mode is panic=abort. In this scenario any code
1022/// previously compiled assuming that a function can unwind is still correct, it
1023/// just never happens to actually unwind at runtime.
1024///
1025/// This function's answer to whether or not a function can unwind is quite
1026/// impactful throughout the compiler. This affects things like:
1027///
1028/// * Calling a function which can't unwind means codegen simply ignores any
1029/// associated unwinding cleanup.
1030/// * Calling a function which can unwind from a function which can't unwind
1031/// causes the `abort_unwinding_calls` MIR pass to insert a landing pad that
1032/// aborts the process.
1033/// * This affects whether functions have the LLVM `nounwind` attribute, which
1034/// affects various optimizations and codegen.
1035///
1036/// FIXME: this is actually buggy with respect to Rust functions. Rust functions
1037/// compiled with `-Cpanic=unwind` and referenced from another crate compiled
1038/// with `-Cpanic=abort` will look like they can't unwind when in fact they
1039/// might (from a foreign exception or similar).
1040#[inline]
1041#[tracing::instrument(level = "debug", skip(tcx))]
1042pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: SpecAbi) -> bool {
1043 if let Some(did) = fn_def_id {
1044 // Special attribute for functions which can't unwind.
1045 if tcx.codegen_fn_attrs(did).flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) {
1046 return false;
1047 }
1048
1049 // With `-C panic=abort`, all non-FFI functions are required to not unwind.
1050 //
1051 // Note that this is true regardless ABI specified on the function -- a `extern "C-unwind"`
1052 // function defined in Rust is also required to abort.
1053 if tcx.sess.panic_strategy() == PanicStrategy::Abort && !tcx.is_foreign_item(did) {
1054 return false;
1055 }
1056
1057 // With -Z panic-in-drop=abort, drop_in_place never unwinds.
1058 //
1059 // This is not part of `codegen_fn_attrs` as it can differ between crates
1060 // and therefore cannot be computed in core.
1061 if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort {
1062 if Some(did) == tcx.lang_items().drop_in_place_fn() {
1063 return false;
1064 }
1065 }
1066 }
1067
1068 // Otherwise if this isn't special then unwinding is generally determined by
1069 // the ABI of the itself. ABIs like `C` have variants which also
1070 // specifically allow unwinding (`C-unwind`), but not all platform-specific
1071 // ABIs have such an option. Otherwise the only other thing here is Rust
1072 // itself, and those ABIs are determined by the panic strategy configured
1073 // for this compilation.
1074 //
1075 // Unfortunately at this time there's also another caveat. Rust [RFC
1076 // 2945][rfc] has been accepted and is in the process of being implemented
1077 // and stabilized. In this interim state we need to deal with historical
1078 // rustc behavior as well as plan for future rustc behavior.
1079 //
1080 // Historically functions declared with `extern "C"` were marked at the
1081 // codegen layer as `nounwind`. This happened regardless of `panic=unwind`
1082 // or not. This is UB for functions in `panic=unwind` mode that then
1083 // actually panic and unwind. Note that this behavior is true for both
1084 // externally declared functions as well as Rust-defined function.
1085 //
1086 // To fix this UB rustc would like to change in the future to catch unwinds
1087 // from function calls that may unwind within a Rust-defined `extern "C"`
1088 // function and forcibly abort the process, thereby respecting the
1089 // `nounwind` attribute emitted for `extern "C"`. This behavior change isn't
1090 // ready to roll out, so determining whether or not the `C` family of ABIs
1091 // unwinds is conditional not only on their definition but also whether the
1092 // `#![feature(c_unwind)]` feature gate is active.
1093 //
1094 // Note that this means that unlike historical compilers rustc now, by
1095 // default, unconditionally thinks that the `C` ABI may unwind. This will
1096 // prevent some optimization opportunities, however, so we try to scope this
1097 // change and only assume that `C` unwinds with `panic=unwind` (as opposed
1098 // to `panic=abort`).
1099 //
1100 // Eventually the check against `c_unwind` here will ideally get removed and
1101 // this'll be a little cleaner as it'll be a straightforward check of the
1102 // ABI.
1103 //
1104 // [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
1105 use SpecAbi::*;
1106 match abi {
1107 C { unwind }
1108 | System { unwind }
1109 | Cdecl { unwind }
1110 | Stdcall { unwind }
1111 | Fastcall { unwind }
1112 | Vectorcall { unwind }
1113 | Thiscall { unwind }
1114 | Aapcs { unwind }
1115 | Win64 { unwind }
1116 | SysV64 { unwind } => {
1117 unwind
1118 || (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind)
1119 }
1120 PtxKernel
1121 | Msp430Interrupt
1122 | X86Interrupt
1123 | AmdGpuKernel
1124 | EfiApi
1125 | AvrInterrupt
1126 | AvrNonBlockingInterrupt
1127 | CCmseNonSecureCall
1128 | Wasm
1129 | RustIntrinsic
1130 | PlatformIntrinsic
1131 | Unadjusted => false,
1132 Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
1133 }
1134}
1135
1136/// Error produced by attempting to compute or adjust a `FnAbi`.
1137#[derive(Copy, Clone, Debug, HashStable)]
1138pub enum FnAbiError<'tcx> {
1139 /// Error produced by a `layout_of` call, while computing `FnAbi` initially.
1140 Layout(LayoutError<'tcx>),
1141
1142 /// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI.
1143 AdjustForForeignAbi(call::AdjustForForeignAbiError),
1144}
1145
1146impl<'tcx> From<LayoutError<'tcx>> for FnAbiError<'tcx> {
1147 fn from(err: LayoutError<'tcx>) -> Self {
1148 Self::Layout(err)
1149 }
1150}
1151
1152impl From<call::AdjustForForeignAbiError> for FnAbiError<'_> {
1153 fn from(err: call::AdjustForForeignAbiError) -> Self {
1154 Self::AdjustForForeignAbi(err)
1155 }
1156}
1157
1158impl<'tcx> fmt::Display for FnAbiError<'tcx> {
1159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1160 match self {
1161 Self::Layout(err) => err.fmt(f),
1162 Self::AdjustForForeignAbi(err) => err.fmt(f),
1163 }
1164 }
1165}
1166
1167impl IntoDiagnostic<'_, !> for FnAbiError<'_> {
1168 fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
1169 handler.struct_fatal(self.to_string())
1170 }
1171}
1172
1173// FIXME(eddyb) maybe use something like this for an unified `fn_abi_of`, not
1174// just for error handling.
1175#[derive(Debug)]
1176pub enum FnAbiRequest<'tcx> {
1177 OfFnPtr { sig: ty::PolyFnSig<'tcx>, extra_args: &'tcx ty::List<Ty<'tcx>> },
1178 OfInstance { instance: ty::Instance<'tcx>, extra_args: &'tcx ty::List<Ty<'tcx>> },
1179}
1180
1181/// Trait for contexts that want to be able to compute `FnAbi`s.
1182/// This automatically gives access to `FnAbiOf`, through a blanket `impl`.
1183pub trait FnAbiOfHelpers<'tcx>: LayoutOfHelpers<'tcx> {
1184 /// The `&FnAbi`-wrapping type (or `&FnAbi` itself), which will be
1185 /// returned from `fn_abi_of_*` (see also `handle_fn_abi_err`).
1186 type FnAbiOfResult: MaybeResult<&'tcx FnAbi<'tcx, Ty<'tcx>>>;
1187
1188 /// Helper used for `fn_abi_of_*`, to adapt `tcx.fn_abi_of_*(...)` into a
1189 /// `Self::FnAbiOfResult` (which does not need to be a `Result<...>`).
1190 ///
1191 /// Most `impl`s, which propagate `FnAbiError`s, should simply return `err`,
1192 /// but this hook allows e.g. codegen to return only `&FnAbi` from its
1193 /// `cx.fn_abi_of_*(...)`, without any `Result<...>` around it to deal with
1194 /// (and any `FnAbiError`s are turned into fatal errors or ICEs).
1195 fn handle_fn_abi_err(
1196 &self,
1197 err: FnAbiError<'tcx>,
1198 span: Span,
1199 fn_abi_request: FnAbiRequest<'tcx>,
1200 ) -> <Self::FnAbiOfResult as MaybeResult<&'tcx FnAbi<'tcx, Ty<'tcx>>>>::Error;
1201}
1202
1203/// Blanket extension trait for contexts that can compute `FnAbi`s.
1204pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
1205 /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
1206 ///
1207 /// NB: this doesn't handle virtual calls - those should use `fn_abi_of_instance`
1208 /// instead, where the instance is an `InstanceDef::Virtual`.
1209 #[inline]
1210 fn fn_abi_of_fn_ptr(
1211 &self,
1212 sig: ty::PolyFnSig<'tcx>,
1213 extra_args: &'tcx ty::List<Ty<'tcx>>,
1214 ) -> Self::FnAbiOfResult {
1215 // FIXME(eddyb) get a better `span` here.
1216 let span = self.layout_tcx_at_span();
1217 let tcx = self.tcx().at(span);
1218
1219 MaybeResult::from(tcx.fn_abi_of_fn_ptr(self.param_env().and((sig, extra_args))).map_err(
1220 |err| self.handle_fn_abi_err(err, span, FnAbiRequest::OfFnPtr { sig, extra_args }),
1221 ))
1222 }
1223
1224 /// Compute a `FnAbi` suitable for declaring/defining an `fn` instance, and for
1225 /// direct calls to an `fn`.
1226 ///
1227 /// NB: that includes virtual calls, which are represented by "direct calls"
1228 /// to an `InstanceDef::Virtual` instance (of `<dyn Trait as Trait>::fn`).
1229 #[inline]
1230 #[tracing::instrument(level = "debug", skip(self))]
1231 fn fn_abi_of_instance(
1232 &self,
1233 instance: ty::Instance<'tcx>,
1234 extra_args: &'tcx ty::List<Ty<'tcx>>,
1235 ) -> Self::FnAbiOfResult {
1236 // FIXME(eddyb) get a better `span` here.
1237 let span = self.layout_tcx_at_span();
1238 let tcx = self.tcx().at(span);
1239
1240 MaybeResult::from(
1241 tcx.fn_abi_of_instance(self.param_env().and((instance, extra_args))).map_err(|err| {
1242 // HACK(eddyb) at least for definitions of/calls to `Instance`s,
1243 // we can get some kind of span even if one wasn't provided.
1244 // However, we don't do this early in order to avoid calling
1245 // `def_span` unconditionally (which may have a perf penalty).
1246 let span = if !span.is_dummy() { span } else { tcx.def_span(instance.def_id()) };
1247 self.handle_fn_abi_err(err, span, FnAbiRequest::OfInstance { instance, extra_args })
1248 }),
1249 )
1250 }
1251}
1252
1253impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {}