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