]>
Commit | Line | Data |
---|---|---|
1 | use crate::fluent_generated as fluent; | |
2 | use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; | |
3 | use crate::ty::normalize_erasing_regions::NormalizationError; | |
4 | use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt}; | |
5 | use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic}; | |
6 | use rustc_hir as hir; | |
7 | use rustc_hir::def_id::DefId; | |
8 | use rustc_index::vec::Idx; | |
9 | use rustc_session::config::OptLevel; | |
10 | use rustc_span::symbol::{sym, Symbol}; | |
11 | use rustc_span::{Span, DUMMY_SP}; | |
12 | use rustc_target::abi::call::FnAbi; | |
13 | use rustc_target::abi::*; | |
14 | use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; | |
15 | ||
16 | use std::cmp::{self}; | |
17 | use std::fmt; | |
18 | use std::num::NonZeroUsize; | |
19 | use std::ops::Bound; | |
20 | ||
21 | pub 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 | ||
34 | impl 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 | ||
121 | pub 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 | ||
126 | impl 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. | |
158 | pub 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. | |
164 | pub 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. | |
171 | pub 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)] | |
176 | pub 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 | ||
186 | impl 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 | ||
197 | impl 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)] | |
209 | pub enum LayoutError<'tcx> { | |
210 | Unknown(Ty<'tcx>), | |
211 | SizeOverflow(Ty<'tcx>), | |
212 | NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>), | |
213 | } | |
214 | ||
215 | impl 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. | |
240 | impl<'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)] | |
258 | pub struct LayoutCx<'tcx, C> { | |
259 | pub tcx: C, | |
260 | pub param_env: ty::ParamEnv<'tcx>, | |
261 | } | |
262 | ||
263 | impl<'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)] | |
280 | pub 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 | ||
295 | impl<'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 | ||
418 | pub trait HasTyCtxt<'tcx>: HasDataLayout { | |
419 | fn tcx(&self) -> TyCtxt<'tcx>; | |
420 | } | |
421 | ||
422 | pub trait HasParamEnv<'tcx> { | |
423 | fn param_env(&self) -> ty::ParamEnv<'tcx>; | |
424 | } | |
425 | ||
426 | impl<'tcx> HasDataLayout for TyCtxt<'tcx> { | |
427 | #[inline] | |
428 | fn data_layout(&self) -> &TargetDataLayout { | |
429 | &self.data_layout | |
430 | } | |
431 | } | |
432 | ||
433 | impl<'tcx> HasTargetSpec for TyCtxt<'tcx> { | |
434 | fn target_spec(&self) -> &Target { | |
435 | &self.sess.target | |
436 | } | |
437 | } | |
438 | ||
439 | impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { | |
440 | #[inline] | |
441 | fn tcx(&self) -> TyCtxt<'tcx> { | |
442 | *self | |
443 | } | |
444 | } | |
445 | ||
446 | impl<'tcx> HasDataLayout for ty::query::TyCtxtAt<'tcx> { | |
447 | #[inline] | |
448 | fn data_layout(&self) -> &TargetDataLayout { | |
449 | &self.data_layout | |
450 | } | |
451 | } | |
452 | ||
453 | impl<'tcx> HasTargetSpec for ty::query::TyCtxtAt<'tcx> { | |
454 | fn target_spec(&self) -> &Target { | |
455 | &self.sess.target | |
456 | } | |
457 | } | |
458 | ||
459 | impl<'tcx> HasTyCtxt<'tcx> for ty::query::TyCtxtAt<'tcx> { | |
460 | #[inline] | |
461 | fn tcx(&self) -> TyCtxt<'tcx> { | |
462 | **self | |
463 | } | |
464 | } | |
465 | ||
466 | impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> { | |
467 | fn param_env(&self) -> ty::ParamEnv<'tcx> { | |
468 | self.param_env | |
469 | } | |
470 | } | |
471 | ||
472 | impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> { | |
473 | fn data_layout(&self) -> &TargetDataLayout { | |
474 | self.tcx.data_layout() | |
475 | } | |
476 | } | |
477 | ||
478 | impl<'tcx, T: HasTargetSpec> HasTargetSpec for LayoutCx<'tcx, T> { | |
479 | fn target_spec(&self) -> &Target { | |
480 | self.tcx.target_spec() | |
481 | } | |
482 | } | |
483 | ||
484 | impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { | |
485 | fn tcx(&self) -> TyCtxt<'tcx> { | |
486 | self.tcx.tcx() | |
487 | } | |
488 | } | |
489 | ||
490 | pub 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 | ||
497 | impl<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 | ||
508 | impl<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 | ||
519 | pub 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`. | |
523 | pub 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. | |
551 | pub 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 | ||
575 | impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {} | |
576 | ||
577 | impl<'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 | ||
586 | impl<'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 | ||
600 | impl<'tcx, C> TyAbiInterface<'tcx, C> for Ty<'tcx> | |
601 | where | |
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))] | |
1042 | pub 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)] | |
1138 | pub 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 | ||
1146 | impl<'tcx> From<LayoutError<'tcx>> for FnAbiError<'tcx> { | |
1147 | fn from(err: LayoutError<'tcx>) -> Self { | |
1148 | Self::Layout(err) | |
1149 | } | |
1150 | } | |
1151 | ||
1152 | impl From<call::AdjustForForeignAbiError> for FnAbiError<'_> { | |
1153 | fn from(err: call::AdjustForForeignAbiError) -> Self { | |
1154 | Self::AdjustForForeignAbi(err) | |
1155 | } | |
1156 | } | |
1157 | ||
1158 | impl<'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 | ||
1167 | impl 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)] | |
1176 | pub 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`. | |
1183 | pub 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. | |
1204 | pub 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 | ||
1253 | impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {} |