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