diff options
Diffstat (limited to 'compiler/rustc_ty_utils/src/layout.rs')
| -rw-r--r-- | compiler/rustc_ty_utils/src/layout.rs | 252 |
1 files changed, 112 insertions, 140 deletions
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index da1eba68d53..b840ff184e0 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -3,7 +3,7 @@ use rustc_hir as hir; use rustc_index::bit_set::BitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::{GeneratorLayout, GeneratorSavedLocal}; -use rustc_middle::query::{LocalCrate, Providers}; +use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, }; @@ -24,28 +24,32 @@ use crate::errors::{ use crate::layout_sanity_check::sanity_check_layout; pub fn provide(providers: &mut Providers) { - *providers = Providers { layout_of, reference_niches_policy, ..*providers }; + *providers = Providers { layout_of, ..*providers }; } -#[instrument(skip(tcx), level = "debug")] -fn reference_niches_policy<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> ReferenceNichePolicy { - tcx.sess.opts.unstable_opts.reference_niches.unwrap_or(DEFAULT_REF_NICHES) -} - -/// The reference niche policy for builtin types, and for types in -/// crates not specifying `-Z reference-niches`. -const DEFAULT_REF_NICHES: ReferenceNichePolicy = ReferenceNichePolicy { size: false, align: false }; - #[instrument(skip(tcx, query), level = "debug")] fn layout_of<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, ) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> { - let (param_env, unnormalized_ty) = query.into_parts(); + let (param_env, ty) = query.into_parts(); + debug!(?ty); + let param_env = param_env.with_reveal_all_normalized(tcx); - // `naive_layout_of` takes care of normalizing the type. - let naive = tcx.naive_layout_of(query)?; - let ty = naive.ty; + let unnormalized_ty = ty; + + // FIXME: We might want to have two different versions of `layout_of`: + // One that can be called after typecheck has completed and can use + // `normalize_erasing_regions` here and another one that can be called + // before typecheck has completed and uses `try_normalize_erasing_regions`. + let ty = match tcx.try_normalize_erasing_regions(param_env, ty) { + Ok(t) => t, + Err(normalization_error) => { + return Err(tcx + .arena + .alloc(LayoutError::NormalizationFailure(ty, normalization_error))); + } + }; if ty != unnormalized_ty { // Ensure this layout is also cached for the normalized type. @@ -53,11 +57,13 @@ fn layout_of<'tcx>( } let cx = LayoutCx { tcx, param_env }; - let layout = layout_of_uncached(&cx, ty)?; + let layout = layout_of_uncached(&cx, ty)?; let layout = TyAndLayout { ty, layout }; + record_layout_for_printing(&cx, layout); - sanity_check_layout(&cx, &layout, &naive); + + sanity_check_layout(&cx, &layout); Ok(layout) } @@ -77,10 +83,12 @@ fn univariant_uninterned<'tcx>( kind: StructKind, ) -> Result<LayoutS, &'tcx LayoutError<'tcx>> { let dl = cx.data_layout(); - assert!( - !(repr.pack.is_some() && repr.align.is_some()), - "already rejected by `naive_layout_of`" - ); + let pack = repr.pack; + if pack.is_some() && repr.align.is_some() { + cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned"); + return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty))); + } + cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty))) } @@ -138,35 +146,75 @@ fn layout_of_uncached<'tcx>( ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA)); if !ty.is_unsafe_ptr() { - // Calling `layout_of` here would cause a query cycle for recursive types; - // so use a conservative estimate that doesn't look past references. - let naive = cx.naive_layout_of(pointee)?.layout; - - let niches = match *pointee.kind() { - ty::FnDef(def, ..) - | ty::Foreign(def) - | ty::Generator(def, ..) - | ty::Closure(def, ..) => tcx.reference_niches_policy(def.krate), - ty::Adt(def, _) => tcx.reference_niches_policy(def.did().krate), - _ => DEFAULT_REF_NICHES, + data_ptr.valid_range_mut().start = 1; + } + + let pointee = tcx.normalize_erasing_regions(param_env, pointee); + if pointee.is_sized(tcx, param_env) { + return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); + } + + let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() + // Projection eagerly bails out when the pointee references errors, + // fall back to structurally deducing metadata. + && !pointee.references_error() + { + let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]); + let metadata_ty = match tcx.try_normalize_erasing_regions( + param_env, + pointee_metadata, + ) { + Ok(metadata_ty) => metadata_ty, + Err(mut err) => { + // Usually `<Ty as Pointee>::Metadata` can't be normalized because + // its struct tail cannot be normalized either, so try to get a + // more descriptive layout error here, which will lead to less confusing + // diagnostics. + match tcx.try_normalize_erasing_regions( + param_env, + tcx.struct_tail_without_normalization(pointee), + ) { + Ok(_) => {}, + Err(better_err) => { + err = better_err; + } + } + return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); + }, }; - let (min_addr, max_addr) = dl.address_range_for( - if niches.size { naive.size } else { Size::ZERO }, - if niches.align { naive.align } else { Align::ONE }, - ); + let metadata_layout = cx.layout_of(metadata_ty)?; + // If the metadata is a 1-zst, then the pointer is thin. + if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 { + return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); + } - *data_ptr.valid_range_mut() = - WrappingRange { start: min_addr.into(), end: max_addr.into() }; - } + let Abi::Scalar(metadata) = metadata_layout.abi else { + return Err(error(cx, LayoutError::Unknown(pointee))); + }; - if let Some(metadata) = ptr_metadata_scalar(cx, pointee)? { - // Effectively a (ptr, meta) tuple. - tcx.mk_layout(cx.scalar_pair(data_ptr, metadata)) + metadata } else { - // No metadata, this is a thin pointer. - tcx.mk_layout(LayoutS::scalar(cx, data_ptr)) - } + let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + + match unsized_part.kind() { + ty::Foreign(..) => { + return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); + } + ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), + ty::Dynamic(..) => { + let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); + vtable.valid_range_mut().start = 1; + vtable + } + _ => { + return Err(error(cx, LayoutError::Unknown(pointee))); + } + } + }; + + // Effectively a (ptr, meta) tuple. + tcx.mk_layout(cx.scalar_pair(data_ptr, metadata)) } ty::Dynamic(_, _, ty::DynStar) => { @@ -178,8 +226,16 @@ fn layout_of_uncached<'tcx>( } // Arrays and slices. - ty::Array(element, count) => { - let count = compute_array_count(cx, count) + ty::Array(element, mut count) => { + if count.has_projections() { + count = tcx.normalize_erasing_regions(param_env, count); + if count.has_projections() { + return Err(error(cx, LayoutError::Unknown(ty))); + } + } + + let count = count + .try_eval_target_usize(tcx, param_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; let element = cx.layout_of(element)?; let size = element @@ -502,104 +558,20 @@ fn layout_of_uncached<'tcx>( } // Types with no meaningful known layout. - ty::Alias(..) - | ty::Bound(..) - | ty::GeneratorWitness(..) - | ty::GeneratorWitnessMIR(..) - | ty::Infer(_) - | ty::Placeholder(..) - | ty::Param(_) - | ty::Error(_) => { - unreachable!("already rejected by `naive_layout_of`"); + ty::Alias(..) => { + // NOTE(eddyb) `layout_of` query should've normalized these away, + // if that was possible, so there's no reason to try again here. + return Err(error(cx, LayoutError::Unknown(ty))); } - }) -} -pub(crate) fn compute_array_count<'tcx>( - cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, - mut count: ty::Const<'tcx>, -) -> Option<u64> { - let LayoutCx { tcx, param_env } = *cx; - if count.has_projections() { - count = tcx.normalize_erasing_regions(param_env, count); - if count.has_projections() { - return None; + ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => { + bug!("Layout::compute: unexpected type `{}`", ty) } - } - - count.try_eval_target_usize(tcx, param_env) -} - -pub(crate) fn ptr_metadata_scalar<'tcx>( - cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, - pointee: Ty<'tcx>, -) -> Result<Option<Scalar>, &'tcx LayoutError<'tcx>> { - let dl = cx.data_layout(); - let scalar_unit = |value: Primitive| { - let size = value.size(dl); - assert!(size.bits() <= 128); - Scalar::Initialized { value, valid_range: WrappingRange::full(size) } - }; - let LayoutCx { tcx, param_env } = *cx; - - let pointee = tcx.normalize_erasing_regions(param_env, pointee); - if pointee.is_sized(tcx, param_env) { - return Ok(None); - } - - if let Some(metadata_def_id) = tcx.lang_items().metadata_type() - // Projection eagerly bails out when the pointee references errors, - // fall back to structurally deducing metadata. - && !pointee.references_error() - { - let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]); - let metadata_ty = match tcx.try_normalize_erasing_regions( - param_env, - pointee_metadata, - ) { - Ok(metadata_ty) => metadata_ty, - Err(mut err) => { - // Usually `<Ty as Pointee>::Metadata` can't be normalized because - // its struct tail cannot be normalized either, so try to get a - // more descriptive layout error here, which will lead to less confusing - // diagnostics. - match tcx.try_normalize_erasing_regions( - param_env, - tcx.struct_tail_without_normalization(pointee), - ) { - Ok(_) => {}, - Err(better_err) => { - err = better_err; - } - } - return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); - }, - }; - - let metadata_layout = cx.layout_of(metadata_ty)?; - - if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 { - Ok(None) // If the metadata is a 1-zst, then the pointer is thin. - } else if let Abi::Scalar(metadata) = metadata_layout.abi { - Ok(Some(metadata)) - } else { - Err(error(cx, LayoutError::Unknown(pointee))) - } - } else { - let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - - match unsized_part.kind() { - ty::Foreign(..) => Ok(None), - ty::Slice(_) | ty::Str => Ok(Some(scalar_unit(Int(dl.ptr_sized_integer(), false)))), - ty::Dynamic(..) => { - let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); - vtable.valid_range_mut().start = 1; - Ok(Some(vtable)) - } - _ => Err(error(cx, LayoutError::Unknown(pointee))), + ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => { + return Err(error(cx, LayoutError::Unknown(ty))); } - } + }) } /// Overlap eligibility and variant assignment for each GeneratorSavedLocal. |
