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