]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/ty/layout.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / ty / layout.rs
CommitLineData
9ffffee4 1use crate::fluent_generated as fluent;
ba9703b0 2use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
a2a8927a 3use crate::ty::normalize_erasing_regions::NormalizationError;
9ffffee4 4use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
2b03887a 5use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic};
dfeec247 6use rustc_hir as hir;
04454e1e 7use rustc_hir::def_id::DefId;
2b03887a
FG
8use rustc_index::vec::Idx;
9use rustc_session::config::OptLevel;
9ffffee4 10use rustc_span::symbol::{sym, Symbol};
c295e0f8 11use rustc_span::{Span, DUMMY_SP};
2b03887a 12use rustc_target::abi::call::FnAbi;
ba9703b0 13use rustc_target::abi::*;
c295e0f8 14use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
ba9703b0 15
2b03887a 16use std::cmp::{self};
ba9703b0 17use std::fmt;
ba9703b0
XL
18use std::num::NonZeroUsize;
19use std::ops::Bound;
48663c56 20
83c7162d 21pub trait IntegerExt {
dc9dc135 22 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>;
5869c6ff
XL
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;
dc9dc135
XL
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);
54a0048b
SL
32}
33
83c7162d 34impl IntegerExt for Integer {
94222f64 35 #[inline]
dc9dc135 36 fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> {
9e0c209e 37 match (*self, signed) {
9e0c209e
SL
38 (I8, false) => tcx.types.u8,
39 (I16, false) => tcx.types.u16,
40 (I32, false) => tcx.types.u32,
41 (I64, false) => tcx.types.u64,
32a655c1 42 (I128, false) => tcx.types.u128,
9e0c209e
SL
43 (I8, true) => tcx.types.i8,
44 (I16, true) => tcx.types.i16,
45 (I32, true) => tcx.types.i32,
46 (I64, true) => tcx.types.i64,
32a655c1 47 (I128, true) => tcx.types.i128,
9e0c209e
SL
48 }
49 }
50
5869c6ff
XL
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,
2b03887a
FG
67 ty::UintTy::U128 => I128,
68 ty::UintTy::Usize => cx.data_layout().ptr_sized_integer(),
dc9dc135 69 }
2b03887a 70 }
dc9dc135 71
2b03887a
FG
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));
416331ca 89
2b03887a
FG
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 \
9ffffee4 96 discriminant range of enum `{}`",
2b03887a
FG
97 ty
98 )
dc9dc135 99 }
2b03887a 100 return (discr, ity.is_signed());
dc9dc135 101 }
dc9dc135 102
2b03887a
FG
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
dc9dc135
XL
110 };
111
2b03887a
FG
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}
416331ca 120
2b03887a
FG
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}
dc9dc135 125
2b03887a
FG
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,
9ffffee4
FG
133 // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
134 Pointer(_) => tcx.mk_mut_ptr(tcx.mk_unit()),
2b03887a 135 }
dc9dc135 136 }
7cac9316 137
2b03887a
FG
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),
9ffffee4
FG
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 }
2b03887a 149 F32 | F64 => bug!("floats do not have an int type"),
532ac7d7
XL
150 }
151 }
2b03887a 152}
532ac7d7 153
2b03887a
FG
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;
7cac9316 159
2b03887a
FG
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;
7cac9316 165
2b03887a
FG
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;
7cac9316 172
9ffffee4
FG
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
2b03887a
FG
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}
7cac9316 214
487cf647
FG
215impl IntoDiagnostic<'_, !> for LayoutError<'_> {
216 fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
2b03887a 217 let mut diag = handler.struct_fatal("");
7cac9316 218
2b03887a
FG
219 match self {
220 LayoutError::Unknown(ty) => {
221 diag.set_arg("ty", ty);
9ffffee4 222 diag.set_primary_message(fluent::middle_unknown_layout);
7cac9316 223 }
2b03887a
FG
224 LayoutError::SizeOverflow(ty) => {
225 diag.set_arg("ty", ty);
9ffffee4 226 diag.set_primary_message(fluent::middle_values_too_big);
2b03887a
FG
227 }
228 LayoutError::NormalizationFailure(ty, e) => {
229 diag.set_arg("ty", ty);
230 diag.set_arg("failure_ty", e.get_type_for_failure());
9ffffee4 231 diag.set_primary_message(fluent::middle_cannot_be_normalized);
7cac9316 232 }
2b03887a
FG
233 }
234 diag
235 }
236}
7cac9316 237
2b03887a
FG
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)
7cac9316 246 }
2b03887a
FG
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 ),
7cac9316
XL
253 }
254 }
54a0048b
SL
255}
256
2b03887a
FG
257#[derive(Clone, Copy)]
258pub struct LayoutCx<'tcx, C> {
259 pub tcx: C,
260 pub param_env: ty::ParamEnv<'tcx>,
261}
262
487cf647
FG
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
0731742a 275/// Type size "skeleton", i.e., the only information determining a type's size.
54a0048b
SL
276/// While this is conservative, (aside from constant sizes, only pointers,
277/// newtypes thereof and null pointer optimized enums are allowed), it is
a1dfa0c6 278/// enough to statically check common use cases of transmute.
54a0048b
SL
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 {
3b2f2976 286 /// If true, this pointer is never null.
54a0048b 287 non_zero: bool,
3b2f2976
XL
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.
dfeec247
XL
291 tail: Ty<'tcx>,
292 },
54a0048b
SL
293}
294
dc9dc135
XL
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>> {
2b03887a 301 debug_assert!(!ty.has_non_region_infer());
54a0048b
SL
302
303 // First try computing a static layout.
2c00a5a8 304 let err = match tcx.layout_of(param_env.and(ty)) {
54a0048b 305 Ok(layout) => {
ff7c6d11 306 return Ok(SizeSkeleton::Known(layout.size));
54a0048b 307 }
dfeec247 308 Err(err) => err,
54a0048b
SL
309 };
310
1b1a35ee 311 match *ty.kind() {
dfeec247 312 ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
ff7c6d11 313 let non_zero = !ty.is_unsafe_ptr();
416331ca 314 let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
1b1a35ee 315 match tail.kind() {
9c376795 316 ty::Param(_) | ty::Alias(ty::Projection, _) => {
2b03887a 317 debug_assert!(tail.has_non_region_param());
fc512014 318 Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
ff7c6d11 319 }
dfeec247
XL
320 _ => bug!(
321 "SizeSkeleton::compute({}): layout errored ({}), yet \
ff7c6d11 322 tail `{}` is not a type parameter or a projection",
dfeec247
XL
323 ty,
324 err,
325 tail
326 ),
ff7c6d11 327 }
54a0048b
SL
328 }
329
b7449926 330 ty::Adt(def, substs) => {
54a0048b 331 // Only newtypes and enums w/ nullable pointer optimization.
5e7ed085 332 if def.is_union() || def.variants().is_empty() || def.variants().len() > 2 {
54a0048b
SL
333 return Err(err);
334 }
335
336 // Get a zero-sized variant or a pointer newtype.
a1dfa0c6
XL
337 let zero_or_ptr_variant = |i| {
338 let i = VariantIdx::new(i);
5e7ed085
FG
339 let fields =
340 def.variant(i).fields.iter().map(|field| {
341 SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)
342 });
54a0048b
SL
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 }
dfeec247 352 SizeSkeleton::Pointer { .. } => {
54a0048b
SL
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.
5e7ed085 365 if def.variants().len() == 1 {
54a0048b
SL
366 if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
367 return Ok(SizeSkeleton::Pointer {
dfeec247 368 non_zero: non_zero
5e7ed085 369 || match tcx.layout_scalar_valid_range(def.did()) {
dfeec247
XL
370 (Bound::Included(start), Bound::Unbounded) => start > 0,
371 (Bound::Included(start), Bound::Included(end)) => {
372 0 < start && start < end
373 }
374 _ => false,
375 },
041b39d2 376 tail,
54a0048b
SL
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) {
dfeec247
XL
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 })
54a0048b 389 }
dfeec247 390 _ => Err(err),
54a0048b
SL
391 }
392 }
393
9c376795 394 ty::Alias(..) => {
0531ce1d 395 let normalized = tcx.normalize_erasing_regions(param_env, ty);
5bcae85e
SL
396 if ty == normalized {
397 Err(err)
398 } else {
7cac9316 399 SizeSkeleton::compute(normalized, tcx, param_env)
5bcae85e
SL
400 }
401 }
402
dfeec247 403 _ => Err(err),
54a0048b
SL
404 }
405 }
406
923072b8 407 pub fn same_size(self, other: SizeSkeleton<'tcx>) -> bool {
54a0048b
SL
408 match (self, other) {
409 (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b,
dfeec247
XL
410 (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
411 a == b
412 }
413 _ => false,
54a0048b
SL
414 }
415 }
416}
cc61c64b 417
ff7c6d11 418pub trait HasTyCtxt<'tcx>: HasDataLayout {
dc9dc135 419 fn tcx(&self) -> TyCtxt<'tcx>;
cc61c64b
XL
420}
421
48663c56
XL
422pub trait HasParamEnv<'tcx> {
423 fn param_env(&self) -> ty::ParamEnv<'tcx>;
424}
425
dc9dc135 426impl<'tcx> HasDataLayout for TyCtxt<'tcx> {
94222f64 427 #[inline]
ff7c6d11
XL
428 fn data_layout(&self) -> &TargetDataLayout {
429 &self.data_layout
430 }
cc61c64b
XL
431}
432
c295e0f8
XL
433impl<'tcx> HasTargetSpec for TyCtxt<'tcx> {
434 fn target_spec(&self) -> &Target {
435 &self.sess.target
436 }
437}
438
dc9dc135 439impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> {
94222f64 440 #[inline]
dc9dc135 441 fn tcx(&self) -> TyCtxt<'tcx> {
e74abb32 442 *self
cc61c64b
XL
443 }
444}
445
94222f64
XL
446impl<'tcx> HasDataLayout for ty::query::TyCtxtAt<'tcx> {
447 #[inline]
448 fn data_layout(&self) -> &TargetDataLayout {
449 &self.data_layout
450 }
451}
452
c295e0f8
XL
453impl<'tcx> HasTargetSpec for ty::query::TyCtxtAt<'tcx> {
454 fn target_spec(&self) -> &Target {
455 &self.sess.target
456 }
457}
458
94222f64
XL
459impl<'tcx> HasTyCtxt<'tcx> for ty::query::TyCtxtAt<'tcx> {
460 #[inline]
461 fn tcx(&self) -> TyCtxt<'tcx> {
462 **self
463 }
464}
465
48663c56
XL
466impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> {
467 fn param_env(&self) -> ty::ParamEnv<'tcx> {
468 self.param_env
469 }
470}
471
2c00a5a8 472impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> {
cc61c64b 473 fn data_layout(&self) -> &TargetDataLayout {
2c00a5a8 474 self.tcx.data_layout()
cc61c64b
XL
475 }
476}
477
c295e0f8
XL
478impl<'tcx, T: HasTargetSpec> HasTargetSpec for LayoutCx<'tcx, T> {
479 fn target_spec(&self) -> &Target {
480 self.tcx.target_spec()
481 }
482}
483
dc9dc135
XL
484impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
485 fn tcx(&self) -> TyCtxt<'tcx> {
2c00a5a8 486 self.tcx.tcx()
ff7c6d11
XL
487 }
488}
489
c295e0f8
XL
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
29967ef6 519pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>;
cc61c64b 520
c295e0f8
XL
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}
ff7c6d11 549
c295e0f8
XL
550/// Blanket extension trait for contexts that can compute layouts of types.
551pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
ff7c6d11 552 /// Computes the layout of a type. Note that this implicitly
94222f64
XL
553 /// executes in "reveal all" mode, and will normalize the input type.
554 #[inline]
c295e0f8
XL
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
a2a8927a 575impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}
c295e0f8 576
a2a8927a 577impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxt<'tcx>> {
c295e0f8
XL
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
cc61c64b
XL
583 }
584}
585
a2a8927a 586impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> {
c295e0f8 587 type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
cc61c64b 588
2c00a5a8 589 #[inline]
c295e0f8
XL
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
2c00a5a8
XL
597 }
598}
599
94222f64 600impl<'tcx, C> TyAbiInterface<'tcx, C> for Ty<'tcx>
dc9dc135 601where
94222f64 602 C: HasTyCtxt<'tcx> + HasParamEnv<'tcx>,
83c7162d 603{
94222f64 604 fn ty_and_layout_for_variant(
ba9703b0
XL
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 }
ff7c6d11
XL
619
620 Variants::Single { index } => {
94222f64
XL
621 let tcx = cx.tcx();
622 let param_env = cx.param_env();
623
ff7c6d11 624 // Deny calling for_variant more than once for non-Single enums.
94222f64 625 if let Ok(original_layout) = tcx.layout_of(param_env.and(this.ty)) {
ba9703b0 626 assert_eq!(original_layout.variants, Variants::Single { index });
48663c56 627 }
ff7c6d11 628
1b1a35ee 629 let fields = match this.ty.kind() {
5e7ed085 630 ty::Adt(def, _) if def.variants().is_empty() =>
f035d41b 631 bug!("for_variant called on zero-variant enum"),
5e7ed085 632 ty::Adt(def, _) => def.variant(variant_index).fields.len(),
dfeec247 633 _ => bug!(),
ff7c6d11 634 };
9ffffee4 635 tcx.mk_layout(LayoutS {
83c7162d 636 variants: Variants::Single { index: variant_index },
ba9703b0
XL
637 fields: match NonZeroUsize::new(fields) {
638 Some(fields) => FieldsShape::Union(fields),
639 None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] },
640 },
83c7162d 641 abi: Abi::Uninhabited,
416331ca 642 largest_niche: None,
83c7162d 643 align: tcx.data_layout.i8_align,
dfeec247 644 size: Size::ZERO,
83c7162d 645 })
ff7c6d11 646 }
cc61c64b 647
9ffffee4 648 Variants::Multiple { ref variants, .. } => cx.tcx().mk_layout(variants[variant_index].clone()),
ff7c6d11
XL
649 };
650
5e7ed085 651 assert_eq!(*layout.variants(), Variants::Single { index: variant_index });
cc61c64b 652
ba9703b0 653 TyAndLayout { ty: this.ty, layout }
cc61c64b
XL
654 }
655
94222f64
XL
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>),
fc512014 660 }
48663c56 661
a2a8927a 662 fn field_ty_or_layout<'tcx>(
fc512014 663 this: TyAndLayout<'tcx>,
94222f64 664 cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>),
fc512014 665 i: usize,
94222f64 666 ) -> TyMaybeWithLayout<'tcx> {
fc512014 667 let tcx = cx.tcx();
c295e0f8 668 let tag_layout = |tag: Scalar| -> TyAndLayout<'tcx> {
5e7ed085 669 TyAndLayout {
9ffffee4 670 layout: tcx.mk_layout(LayoutS::scalar(cx, tag)),
04454e1e 671 ty: tag.primitive().to_ty(tcx),
5e7ed085 672 }
fc512014 673 };
ff7c6d11 674
94222f64 675 match *this.ty.kind() {
fc512014
XL
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(..)
9ffffee4 685 | ty::GeneratorWitnessMIR(..)
fc512014 686 | ty::Foreign(..)
f2b60f7d
FG
687 | ty::Dynamic(_, _, ty::Dyn) => {
688 bug!("TyAndLayout::field({:?}): not applicable", this)
689 }
fc512014
XL
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();
94222f64 701 let unit_ptr_ty = if this.ty.is_unsafe_ptr() {
fc512014
XL
702 tcx.mk_mut_ptr(nil)
703 } else {
704 tcx.mk_mut_ref(tcx.lifetimes.re_static, nil)
705 };
94222f64
XL
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 });
ff7c6d11 714 }
cc61c64b 715
9c376795
FG
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
fc512014 750 }
9c376795
FG
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)
48663c56 760 }
ea8adc8c 761
fc512014
XL
762 // Arrays and slices.
763 ty::Array(element, _) | ty::Slice(element) => TyMaybeWithLayout::Ty(element),
764 ty::Str => TyMaybeWithLayout::Ty(tcx.types.u8),
cc61c64b 765
fc512014 766 // Tuples, generators and closures.
94222f64
XL
767 ty::Closure(_, ref substs) => field_ty_or_layout(
768 TyAndLayout { ty: substs.as_closure().tupled_upvars_ty(), ..this },
769 cx,
770 i,
771 ),
cc61c64b 772
fc512014
XL
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 ),
c295e0f8 783 Variants::Multiple { tag, tag_field, .. } => {
fc512014
XL
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
5e7ed085 791 ty::Tuple(tys) => TyMaybeWithLayout::Ty(tys[i]),
ff7c6d11 792
fc512014
XL
793 // ADTs.
794 ty::Adt(def, substs) => {
795 match this.variants {
796 Variants::Single { index } => {
5e7ed085 797 TyMaybeWithLayout::Ty(def.variant(index).fields[i].ty(tcx, substs))
fc512014
XL
798 }
799
800 // Discriminant field for enums (where applicable).
c295e0f8 801 Variants::Multiple { tag, .. } => {
fc512014
XL
802 assert_eq!(i, 0);
803 return TyMaybeWithLayout::TyAndLayout(tag_layout(tag));
804 }
ff7c6d11
XL
805 }
806 }
fc512014 807
f2b60f7d
FG
808 ty::Dynamic(_, _, ty::DynStar) => {
809 if i == 0 {
9ffffee4 810 TyMaybeWithLayout::Ty(tcx.mk_mut_ptr(tcx.types.unit))
f2b60f7d
FG
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
9c376795 824 ty::Alias(..)
fc512014
XL
825 | ty::Bound(..)
826 | ty::Placeholder(..)
fc512014
XL
827 | ty::Param(_)
828 | ty::Infer(_)
94222f64 829 | ty::Error(_) => bug!("TyAndLayout::field: unexpected type `{}`", this.ty),
cc61c64b 830 }
fc512014 831 }
cc61c64b 832
94222f64
XL
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 }
ff7c6d11 848 }
48663c56 849
94222f64
XL
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
1b1a35ee 858 let pointee_info = match *this.ty.kind() {
48663c56 859 ty::RawPtr(mt) if offset.bytes() == 0 => {
94222f64 860 tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
dfeec247
XL
861 size: layout.size,
862 align: layout.align.abi,
863 safe: None,
3dfed10e
XL
864 })
865 }
866 ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
94222f64
XL
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,
dfeec247 871 })
48663c56 872 }
48663c56 873 ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
9ffffee4
FG
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 },
48663c56
XL
885 };
886
94222f64 887 tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
dfeec247
XL
888 size: layout.size,
889 align: layout.align.abi,
890 safe: Some(kind),
891 })
48663c56
XL
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
9c376795 902 // validity range encodes). This allows using
48663c56
XL
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 {
f2b60f7d 908 tag_encoding: TagEncoding::Niche { untagged_variant, .. },
f035d41b 909 tag_field,
48663c56 910 ..
f035d41b 911 } if this.fields.offset(tag_field) == offset => {
f2b60f7d 912 Some(this.for_variant(cx, untagged_variant))
dfeec247 913 }
48663c56
XL
914 _ => Some(this),
915 };
916
917 if let Some(variant) = data_variant {
918 // We're not interested in any unions.
ba9703b0 919 if let FieldsShape::Union(_) = variant.fields {
48663c56
XL
920 data_variant = None;
921 }
922 }
923
924 let mut result = None;
925
926 if let Some(variant) = data_variant {
9ffffee4
FG
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);
48663c56
XL
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);
dfeec247
XL
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.
3dfed10e
XL
937 let field_info =
938 field.pointee_info_at(cx, offset - field_start);
939 field_info
dfeec247
XL
940 } else {
941 None
942 }
943 });
48663c56
XL
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 {
1b1a35ee 953 if let ty::Adt(def, _) = this.ty.kind() {
48663c56 954 if def.is_box() && offset.bytes() == 0 {
9ffffee4
FG
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 });
48663c56
XL
959 }
960 }
961 }
962
963 result
964 }
3dfed10e
XL
965 };
966
967 debug!(
968 "pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
1b1a35ee
XL
969 offset,
970 this.ty.kind(),
971 pointee_info
3dfed10e
XL
972 );
973
974 pointee_info
48663c56 975 }
04454e1e
FG
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 }
83c7162d 992}
ff7c6d11 993
94222f64
XL
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///
5e7ed085 1017/// Rust functions are classified whether or not they can unwind based on the
94222f64
XL
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]
f2b60f7d 1041#[tracing::instrument(level = "debug", skip(tcx))]
9c376795 1042pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: SpecAbi) -> bool {
04454e1e
FG
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
923072b8
FG
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
04454e1e
FG
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.
064997fb 1061 if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort {
04454e1e
FG
1062 if Some(did) == tcx.lang_items().drop_in_place_fn() {
1063 return false;
1064 }
1065 }
94222f64
XL
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
5e7ed085 1089 // `nounwind` attribute emitted for `extern "C"`. This behavior change isn't
94222f64
XL
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 {
5099ac24
FG
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 } => {
94222f64
XL
1117 unwind
1118 || (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind)
ba9703b0 1119 }
5099ac24 1120 PtxKernel
94222f64
XL
1121 | Msp430Interrupt
1122 | X86Interrupt
1123 | AmdGpuKernel
1124 | EfiApi
1125 | AvrInterrupt
1126 | AvrNonBlockingInterrupt
1127 | CCmseNonSecureCall
1128 | Wasm
1129 | RustIntrinsic
1130 | PlatformIntrinsic
1131 | Unadjusted => false,
923072b8 1132 Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
ba9703b0
XL
1133 }
1134}
1135
c295e0f8 1136/// Error produced by attempting to compute or adjust a `FnAbi`.
5099ac24 1137#[derive(Copy, Clone, Debug, HashStable)]
c295e0f8
XL
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
a2a8927a 1146impl<'tcx> From<LayoutError<'tcx>> for FnAbiError<'tcx> {
c295e0f8
XL
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)
48663c56 1155 }
c295e0f8 1156}
48663c56 1157
c295e0f8
XL
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}
48663c56 1166
487cf647
FG
1167impl IntoDiagnostic<'_, !> for FnAbiError<'_> {
1168 fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
2b03887a
FG
1169 handler.struct_fatal(self.to_string())
1170 }
1171}
1172
c295e0f8
XL
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}
60c5eb7d 1180
c295e0f8
XL
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>>>;
ba9703b0 1187
c295e0f8
XL
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]
f2b60f7d 1230 #[tracing::instrument(level = "debug", skip(self))]
c295e0f8
XL
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 }),
5869c6ff 1249 )
48663c56 1250 }
c295e0f8 1251}
48663c56 1252
a2a8927a 1253impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {}