summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_middle/src/ty/layout.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:03:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:03:36 +0000
commit17d40c6057c88f4c432b0d7bac88e1b84cb7e67f (patch)
tree3f66c4a5918660bb8a758ab6cda5ff8ee4f6cdcd /compiler/rustc_middle/src/ty/layout.rs
parentAdding upstream version 1.64.0+dfsg1. (diff)
downloadrustc-upstream/1.65.0+dfsg1.tar.xz
rustc-upstream/1.65.0+dfsg1.zip
Adding upstream version 1.65.0+dfsg1.upstream/1.65.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_middle/src/ty/layout.rs')
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs720
1 files changed, 362 insertions, 358 deletions
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index ad78d24e9..042eeec3f 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2,7 +2,10 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::mir::{GeneratorLayout, GeneratorSavedLocal};
use crate::ty::normalize_erasing_regions::NormalizationError;
use crate::ty::subst::Subst;
-use crate::ty::{self, subst::SubstsRef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeVisitable};
+use crate::ty::{
+ self, layout_sanity_check::sanity_check_layout, subst::SubstsRef, EarlyBinder, ReprOptions, Ty,
+ TyCtxt, TypeVisitable,
+};
use rustc_ast as ast;
use rustc_attr as attr;
use rustc_hir as hir;
@@ -19,7 +22,7 @@ use rustc_target::abi::call::{
use rustc_target::abi::*;
use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
-use std::cmp;
+use std::cmp::{self, Ordering};
use std::fmt;
use std::iter;
use std::num::NonZeroUsize;
@@ -221,164 +224,46 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
}
}
-/// Enforce some basic invariants on layouts.
-fn sanity_check_layout<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- layout: &TyAndLayout<'tcx>,
-) {
- // Type-level uninhabitedness should always imply ABI uninhabitedness.
- if tcx.conservative_is_privately_uninhabited(param_env.and(layout.ty)) {
- assert!(layout.abi.is_uninhabited());
- }
-
- if layout.size.bytes() % layout.align.abi.bytes() != 0 {
- bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
- }
-
- if cfg!(debug_assertions) {
- fn check_layout_abi<'tcx>(tcx: TyCtxt<'tcx>, layout: Layout<'tcx>) {
- match layout.abi() {
- Abi::Scalar(scalar) => {
- // No padding in scalars.
- assert_eq!(
- layout.align().abi,
- scalar.align(&tcx).abi,
- "alignment mismatch between ABI and layout in {layout:#?}"
- );
- assert_eq!(
- layout.size(),
- scalar.size(&tcx),
- "size mismatch between ABI and layout in {layout:#?}"
- );
- }
- Abi::Vector { count, element } => {
- // No padding in vectors. Alignment can be strengthened, though.
- assert!(
- layout.align().abi >= element.align(&tcx).abi,
- "alignment mismatch between ABI and layout in {layout:#?}"
- );
- let size = element.size(&tcx) * count;
- assert_eq!(
- layout.size(),
- size.align_to(tcx.data_layout().vector_align(size).abi),
- "size mismatch between ABI and layout in {layout:#?}"
- );
- }
- Abi::ScalarPair(scalar1, scalar2) => {
- // Sanity-check scalar pairs. These are a bit more flexible and support
- // padding, but we can at least ensure both fields actually fit into the layout
- // and the alignment requirement has not been weakened.
- let align1 = scalar1.align(&tcx).abi;
- let align2 = scalar2.align(&tcx).abi;
- assert!(
- layout.align().abi >= cmp::max(align1, align2),
- "alignment mismatch between ABI and layout in {layout:#?}",
- );
- let field2_offset = scalar1.size(&tcx).align_to(align2);
- assert!(
- layout.size() >= field2_offset + scalar2.size(&tcx),
- "size mismatch between ABI and layout in {layout:#?}"
- );
- }
- Abi::Uninhabited | Abi::Aggregate { .. } => {} // Nothing to check.
- }
- }
-
- check_layout_abi(tcx, layout.layout);
-
- if let Variants::Multiple { variants, .. } = &layout.variants {
- for variant in variants {
- check_layout_abi(tcx, *variant);
- // No nested "multiple".
- assert!(matches!(variant.variants(), Variants::Single { .. }));
- // Skip empty variants.
- if variant.size() == Size::ZERO
- || variant.fields().count() == 0
- || variant.abi().is_uninhabited()
- {
- // These are never actually accessed anyway, so we can skip them. (Note that
- // sometimes, variants with fields have size 0, and sometimes, variants without
- // fields have non-0 size.)
- continue;
- }
- // Variants should have the same or a smaller size as the full thing.
- if variant.size() > layout.size {
- bug!(
- "Type with size {} bytes has variant with size {} bytes: {layout:#?}",
- layout.size.bytes(),
- variant.size().bytes(),
- )
- }
- // The top-level ABI and the ABI of the variants should be coherent.
- let abi_coherent = match (layout.abi, variant.abi()) {
- (Abi::Scalar(..), Abi::Scalar(..)) => true,
- (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
- (Abi::Uninhabited, _) => true,
- (Abi::Aggregate { .. }, _) => true,
- _ => false,
- };
- if !abi_coherent {
- bug!(
- "Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
- variant
- );
- }
- }
- }
- }
-}
-
#[instrument(skip(tcx, query), level = "debug")]
fn layout_of<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
) -> Result<TyAndLayout<'tcx>, LayoutError<'tcx>> {
- ty::tls::with_related_context(tcx, move |icx| {
- let (param_env, ty) = query.into_parts();
- debug!(?ty);
-
- if !tcx.recursion_limit().value_within_limit(icx.layout_depth) {
- tcx.sess.fatal(&format!("overflow representing the type `{}`", ty));
+ let (param_env, ty) = query.into_parts();
+ debug!(?ty);
+
+ let param_env = param_env.with_reveal_all_normalized(tcx);
+ 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(LayoutError::NormalizationFailure(ty, normalization_error));
}
+ };
- // Update the ImplicitCtxt to increase the layout_depth
- let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() };
-
- ty::tls::enter_context(&icx, |_| {
- let param_env = param_env.with_reveal_all_normalized(tcx);
- 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(LayoutError::NormalizationFailure(ty, normalization_error));
- }
- };
-
- if ty != unnormalized_ty {
- // Ensure this layout is also cached for the normalized type.
- return tcx.layout_of(param_env.and(ty));
- }
+ if ty != unnormalized_ty {
+ // Ensure this layout is also cached for the normalized type.
+ return tcx.layout_of(param_env.and(ty));
+ }
- let cx = LayoutCx { tcx, param_env };
+ let cx = LayoutCx { tcx, param_env };
- let layout = cx.layout_of_uncached(ty)?;
- let layout = TyAndLayout { ty, layout };
+ let layout = cx.layout_of_uncached(ty)?;
+ let layout = TyAndLayout { ty, layout };
- cx.record_layout_for_printing(layout);
+ cx.record_layout_for_printing(layout);
- sanity_check_layout(tcx, param_env, &layout);
+ sanity_check_layout(&cx, &layout);
- Ok(layout)
- })
- })
+ Ok(layout)
}
+#[derive(Clone, Copy)]
pub struct LayoutCx<'tcx, C> {
pub tcx: C,
pub param_env: ty::ParamEnv<'tcx>,
@@ -740,6 +625,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
tcx.intern_layout(self.scalar_pair(data_ptr, metadata))
}
+ ty::Dynamic(_, _, ty::DynStar) => {
+ let mut data = scalar_unit(Int(dl.ptr_sized_integer(), false));
+ data.valid_range_mut().start = 0;
+ let mut vtable = scalar_unit(Pointer);
+ vtable.valid_range_mut().start = 1;
+ tcx.intern_layout(self.scalar_pair(data, vtable))
+ }
+
// Arrays and slices.
ty::Array(element, mut count) => {
if count.has_projections() {
@@ -794,7 +687,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// Odd unit types.
ty::FnDef(..) => univariant(&[], &ReprOptions::default(), StructKind::AlwaysSized)?,
- ty::Dynamic(..) | ty::Foreign(..) => {
+ ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => {
let mut unit = self.univariant_uninterned(
ty,
&[],
@@ -872,7 +765,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// * the element type and length of the single array field, if
// the first field is of array type, or
//
- // * the homogenous field type and the number of fields.
+ // * the homogeneous field type and the number of fields.
let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.kind() {
// First ADT field is an array:
@@ -1161,131 +1054,191 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// that allow representation optimization.)
assert!(def.is_enum());
- // The current code for niche-filling relies on variant indices
- // instead of actual discriminants, so dataful enums with
- // explicit discriminants (RFC #2363) would misbehave.
- let no_explicit_discriminants = def
- .variants()
- .iter_enumerated()
- .all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32()));
-
- let mut niche_filling_layout = None;
-
- // Niche-filling enum optimization.
- if !def.repr().inhibit_enum_layout_opt() && no_explicit_discriminants {
- let mut dataful_variant = None;
- let mut niche_variants = VariantIdx::MAX..=VariantIdx::new(0);
+ // Until we've decided whether to use the tagged or
+ // niche filling LayoutS, we don't want to intern the
+ // variant layouts, so we can't store them in the
+ // overall LayoutS. Store the overall LayoutS
+ // and the variant LayoutSs here until then.
+ struct TmpLayout<'tcx> {
+ layout: LayoutS<'tcx>,
+ variants: IndexVec<VariantIdx, LayoutS<'tcx>>,
+ }
- // Find one non-ZST variant.
- 'variants: for (v, fields) in variants.iter_enumerated() {
- if absent(fields) {
- continue 'variants;
+ let calculate_niche_filling_layout =
+ || -> Result<Option<TmpLayout<'tcx>>, LayoutError<'tcx>> {
+ // The current code for niche-filling relies on variant indices
+ // instead of actual discriminants, so enums with
+ // explicit discriminants (RFC #2363) would misbehave.
+ if def.repr().inhibit_enum_layout_opt()
+ || def
+ .variants()
+ .iter_enumerated()
+ .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
+ {
+ return Ok(None);
}
- for f in fields {
- if !f.is_zst() {
- if dataful_variant.is_none() {
- dataful_variant = Some(v);
- continue 'variants;
- } else {
- dataful_variant = None;
- break 'variants;
- }
- }
+
+ if variants.len() < 2 {
+ return Ok(None);
}
- niche_variants = *niche_variants.start().min(&v)..=v;
- }
- if niche_variants.start() > niche_variants.end() {
- dataful_variant = None;
- }
+ let mut align = dl.aggregate_align;
+ let mut variant_layouts = variants
+ .iter_enumerated()
+ .map(|(j, v)| {
+ let mut st = self.univariant_uninterned(
+ ty,
+ v,
+ &def.repr(),
+ StructKind::AlwaysSized,
+ )?;
+ st.variants = Variants::Single { index: j };
+
+ align = align.max(st.align);
+
+ Ok(st)
+ })
+ .collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
+
+ let largest_variant_index = match variant_layouts
+ .iter_enumerated()
+ .max_by_key(|(_i, layout)| layout.size.bytes())
+ .map(|(i, _layout)| i)
+ {
+ None => return Ok(None),
+ Some(i) => i,
+ };
+
+ let all_indices = VariantIdx::new(0)..=VariantIdx::new(variants.len() - 1);
+ let needs_disc = |index: VariantIdx| {
+ index != largest_variant_index && !absent(&variants[index])
+ };
+ let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap()
+ ..=all_indices.rev().find(|v| needs_disc(*v)).unwrap();
- if let Some(i) = dataful_variant {
- let count = (niche_variants.end().as_u32()
- - niche_variants.start().as_u32()
- + 1) as u128;
+ let count = niche_variants.size_hint().1.unwrap() as u128;
// Find the field with the largest niche
- let niche_candidate = variants[i]
+ let (field_index, niche, (niche_start, niche_scalar)) = match variants
+ [largest_variant_index]
.iter()
.enumerate()
.filter_map(|(j, field)| Some((j, field.largest_niche?)))
- .max_by_key(|(_, niche)| niche.available(dl));
-
- if let Some((field_index, niche, (niche_start, niche_scalar))) =
- niche_candidate.and_then(|(field_index, niche)| {
- Some((field_index, niche, niche.reserve(self, count)?))
- })
+ .max_by_key(|(_, niche)| niche.available(dl))
+ .and_then(|(j, niche)| Some((j, niche, niche.reserve(self, count)?)))
{
- let mut align = dl.aggregate_align;
- let st = variants
- .iter_enumerated()
- .map(|(j, v)| {
- let mut st = self.univariant_uninterned(
- ty,
- v,
- &def.repr(),
- StructKind::AlwaysSized,
- )?;
- st.variants = Variants::Single { index: j };
+ None => return Ok(None),
+ Some(x) => x,
+ };
+
+ let niche_offset = niche.offset
+ + variant_layouts[largest_variant_index].fields.offset(field_index);
+ let niche_size = niche.value.size(dl);
+ let size = variant_layouts[largest_variant_index].size.align_to(align.abi);
- align = align.max(st.align);
+ let all_variants_fit =
+ variant_layouts.iter_enumerated_mut().all(|(i, layout)| {
+ if i == largest_variant_index {
+ return true;
+ }
- Ok(tcx.intern_layout(st))
- })
- .collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
+ layout.largest_niche = None;
- let offset = st[i].fields().offset(field_index) + niche.offset;
+ if layout.size <= niche_offset {
+ // This variant will fit before the niche.
+ return true;
+ }
- // Align the total size to the largest alignment.
- let size = st[i].size().align_to(align.abi);
+ // Determine if it'll fit after the niche.
+ let this_align = layout.align.abi;
+ let this_offset = (niche_offset + niche_size).align_to(this_align);
- let abi = if st.iter().all(|v| v.abi().is_uninhabited()) {
- Abi::Uninhabited
- } else if align == st[i].align() && size == st[i].size() {
- // When the total alignment and size match, we can use the
- // same ABI as the scalar variant with the reserved niche.
- match st[i].abi() {
- Abi::Scalar(_) => Abi::Scalar(niche_scalar),
- Abi::ScalarPair(first, second) => {
- // Only the niche is guaranteed to be initialised,
- // so use union layout for the other primitive.
- if offset.bytes() == 0 {
- Abi::ScalarPair(niche_scalar, second.to_union())
- } else {
- Abi::ScalarPair(first.to_union(), niche_scalar)
+ if this_offset + layout.size > size {
+ return false;
+ }
+
+ // It'll fit, but we need to make some adjustments.
+ match layout.fields {
+ FieldsShape::Arbitrary { ref mut offsets, .. } => {
+ for (j, offset) in offsets.iter_mut().enumerate() {
+ if !variants[i][j].is_zst() {
+ *offset += this_offset;
+ }
}
}
- _ => Abi::Aggregate { sized: true },
+ _ => {
+ panic!("Layout of fields should be Arbitrary for variants")
+ }
}
- } else {
- Abi::Aggregate { sized: true }
- };
- let largest_niche = Niche::from_scalar(dl, offset, niche_scalar);
-
- niche_filling_layout = Some(LayoutS {
- variants: Variants::Multiple {
- tag: niche_scalar,
- tag_encoding: TagEncoding::Niche {
- dataful_variant: i,
- niche_variants,
- niche_start,
- },
- tag_field: 0,
- variants: st,
- },
- fields: FieldsShape::Arbitrary {
- offsets: vec![offset],
- memory_index: vec![0],
- },
- abi,
- largest_niche,
- size,
- align,
+ // It can't be a Scalar or ScalarPair because the offset isn't 0.
+ if !layout.abi.is_uninhabited() {
+ layout.abi = Abi::Aggregate { sized: true };
+ }
+ layout.size += this_offset;
+
+ true
});
+
+ if !all_variants_fit {
+ return Ok(None);
}
- }
- }
+
+ let largest_niche = Niche::from_scalar(dl, niche_offset, niche_scalar);
+
+ let others_zst = variant_layouts.iter_enumerated().all(|(i, layout)| {
+ i == largest_variant_index || layout.size == Size::ZERO
+ });
+ let same_size = size == variant_layouts[largest_variant_index].size;
+ let same_align = align == variant_layouts[largest_variant_index].align;
+
+ let abi = if variant_layouts.iter().all(|v| v.abi.is_uninhabited()) {
+ Abi::Uninhabited
+ } else if same_size && same_align && others_zst {
+ match variant_layouts[largest_variant_index].abi {
+ // When the total alignment and size match, we can use the
+ // same ABI as the scalar variant with the reserved niche.
+ Abi::Scalar(_) => Abi::Scalar(niche_scalar),
+ Abi::ScalarPair(first, second) => {
+ // Only the niche is guaranteed to be initialised,
+ // so use union layouts for the other primitive.
+ if niche_offset == Size::ZERO {
+ Abi::ScalarPair(niche_scalar, second.to_union())
+ } else {
+ Abi::ScalarPair(first.to_union(), niche_scalar)
+ }
+ }
+ _ => Abi::Aggregate { sized: true },
+ }
+ } else {
+ Abi::Aggregate { sized: true }
+ };
+
+ let layout = LayoutS {
+ variants: Variants::Multiple {
+ tag: niche_scalar,
+ tag_encoding: TagEncoding::Niche {
+ untagged_variant: largest_variant_index,
+ niche_variants,
+ niche_start,
+ },
+ tag_field: 0,
+ variants: IndexVec::new(),
+ },
+ fields: FieldsShape::Arbitrary {
+ offsets: vec![niche_offset],
+ memory_index: vec![0],
+ },
+ abi,
+ largest_niche,
+ size,
+ align,
+ };
+
+ Ok(Some(TmpLayout { layout, variants: variant_layouts }))
+ };
+
+ let niche_filling_layout = calculate_niche_filling_layout()?;
let (mut min, mut max) = (i128::MAX, i128::MIN);
let discr_type = def.repr().discr_type();
@@ -1540,15 +1493,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag);
- let layout_variants =
- layout_variants.into_iter().map(|v| tcx.intern_layout(v)).collect();
-
let tagged_layout = LayoutS {
variants: Variants::Multiple {
tag,
tag_encoding: TagEncoding::Direct,
tag_field: 0,
- variants: layout_variants,
+ variants: IndexVec::new(),
},
fields: FieldsShape::Arbitrary {
offsets: vec![Size::ZERO],
@@ -1560,20 +1510,45 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
size,
};
- let best_layout = match (tagged_layout, niche_filling_layout) {
- (tagged_layout, Some(niche_filling_layout)) => {
+ let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants };
+
+ let mut best_layout = match (tagged_layout, niche_filling_layout) {
+ (tl, Some(nl)) => {
// Pick the smaller layout; otherwise,
// pick the layout with the larger niche; otherwise,
// pick tagged as it has simpler codegen.
- cmp::min_by_key(tagged_layout, niche_filling_layout, |layout| {
- let niche_size = layout.largest_niche.map_or(0, |n| n.available(dl));
- (layout.size, cmp::Reverse(niche_size))
- })
+ use Ordering::*;
+ let niche_size = |tmp_l: &TmpLayout<'_>| {
+ tmp_l.layout.largest_niche.map_or(0, |n| n.available(dl))
+ };
+ match (
+ tl.layout.size.cmp(&nl.layout.size),
+ niche_size(&tl).cmp(&niche_size(&nl)),
+ ) {
+ (Greater, _) => nl,
+ (Equal, Less) => nl,
+ _ => tl,
+ }
}
- (tagged_layout, None) => tagged_layout,
+ (tl, None) => tl,
};
- tcx.intern_layout(best_layout)
+ // Now we can intern the variant layouts and store them in the enum layout.
+ best_layout.layout.variants = match best_layout.layout.variants {
+ Variants::Multiple { tag, tag_encoding, tag_field, .. } => Variants::Multiple {
+ tag,
+ tag_encoding,
+ tag_field,
+ variants: best_layout
+ .variants
+ .into_iter()
+ .map(|layout| tcx.intern_layout(layout))
+ .collect(),
+ },
+ _ => bug!(),
+ };
+
+ tcx.intern_layout(best_layout.layout)
}
// Types with no meaningful known layout.
@@ -2468,7 +2443,9 @@ where
| ty::FnDef(..)
| ty::GeneratorWitness(..)
| ty::Foreign(..)
- | ty::Dynamic(..) => bug!("TyAndLayout::field({:?}): not applicable", this),
+ | ty::Dynamic(_, _, ty::Dyn) => {
+ bug!("TyAndLayout::field({:?}): not applicable", this)
+ }
// Potentially-fat pointers.
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
@@ -2497,7 +2474,7 @@ where
match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() {
ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize),
- ty::Dynamic(_, _) => {
+ ty::Dynamic(_, _, ty::Dyn) => {
TyMaybeWithLayout::Ty(tcx.mk_imm_ref(
tcx.lifetimes.re_static,
tcx.mk_array(tcx.types.usize, 3),
@@ -2566,6 +2543,22 @@ where
}
}
+ ty::Dynamic(_, _, ty::DynStar) => {
+ if i == 0 {
+ TyMaybeWithLayout::Ty(tcx.types.usize)
+ } else if i == 1 {
+ // FIXME(dyn-star) same FIXME as above applies here too
+ TyMaybeWithLayout::Ty(
+ tcx.mk_imm_ref(
+ tcx.lifetimes.re_static,
+ tcx.mk_array(tcx.types.usize, 3),
+ ),
+ )
+ } else {
+ bug!("no field {i} on dyn*")
+ }
+ }
+
ty::Projection(_)
| ty::Bound(..)
| ty::Placeholder(..)
@@ -2674,11 +2667,11 @@ where
// using more niches than just null (e.g., the first page of
// the address space, or unaligned pointers).
Variants::Multiple {
- tag_encoding: TagEncoding::Niche { dataful_variant, .. },
+ tag_encoding: TagEncoding::Niche { untagged_variant, .. },
tag_field,
..
} if this.fields.offset(tag_field) == offset => {
- Some(this.for_variant(cx, dataful_variant))
+ Some(this.for_variant(cx, untagged_variant))
}
_ => Some(this),
};
@@ -2761,6 +2754,7 @@ impl<'tcx> ty::Instance<'tcx> {
// for `Instance` (e.g. typeck would use `Ty::fn_sig` instead),
// or should go through `FnAbi` instead, to avoid losing any
// adjustments `fn_abi_of_instance` might be performing.
+ #[tracing::instrument(level = "debug", skip(tcx, param_env))]
fn fn_sig_for_fn_abi(
&self,
tcx: TyCtxt<'tcx>,
@@ -2907,6 +2901,7 @@ impl<'tcx> ty::Instance<'tcx> {
/// with `-Cpanic=abort` will look like they can't unwind when in fact they
/// might (from a foreign exception or similar).
#[inline]
+#[tracing::instrument(level = "debug", skip(tcx))]
pub fn fn_can_unwind<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: Option<DefId>, abi: SpecAbi) -> bool {
if let Some(did) = fn_def_id {
// Special attribute for functions which can't unwind.
@@ -3123,6 +3118,7 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
/// NB: that includes virtual calls, which are represented by "direct calls"
/// to an `InstanceDef::Virtual` instance (of `<dyn Trait as Trait>::fn`).
#[inline]
+ #[tracing::instrument(level = "debug", skip(self))]
fn fn_abi_of_instance(
&self,
instance: ty::Instance<'tcx>,
@@ -3179,9 +3175,100 @@ fn fn_abi_of_instance<'tcx>(
)
}
+// Handle safe Rust thin and fat pointers.
+pub fn adjust_for_rust_scalar<'tcx>(
+ cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+ attrs: &mut ArgAttributes,
+ scalar: Scalar,
+ layout: TyAndLayout<'tcx>,
+ offset: Size,
+ is_return: bool,
+) {
+ // Booleans are always a noundef i1 that needs to be zero-extended.
+ if scalar.is_bool() {
+ attrs.ext(ArgExtension::Zext);
+ attrs.set(ArgAttribute::NoUndef);
+ return;
+ }
+
+ // Scalars which have invalid values cannot be undef.
+ if !scalar.is_always_valid(&cx) {
+ attrs.set(ArgAttribute::NoUndef);
+ }
+
+ // Only pointer types handled below.
+ let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
+
+ if !valid_range.contains(0) {
+ attrs.set(ArgAttribute::NonNull);
+ }
+
+ if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
+ if let Some(kind) = pointee.safe {
+ attrs.pointee_align = Some(pointee.align);
+
+ // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
+ // for the entire duration of the function as they can be deallocated
+ // at any time. Same for shared mutable references. If LLVM had a
+ // way to say "dereferenceable on entry" we could use it here.
+ attrs.pointee_size = match kind {
+ PointerKind::UniqueBorrowed
+ | PointerKind::UniqueBorrowedPinned
+ | PointerKind::Frozen => pointee.size,
+ PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
+ };
+
+ // `Box`, `&T`, and `&mut T` cannot be undef.
+ // Note that this only applies to the value of the pointer itself;
+ // this attribute doesn't make it UB for the pointed-to data to be undef.
+ attrs.set(ArgAttribute::NoUndef);
+
+ // The aliasing rules for `Box<T>` are still not decided, but currently we emit
+ // `noalias` for it. This can be turned off using an unstable flag.
+ // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
+ let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true);
+
+ // `&mut` pointer parameters never alias other parameters,
+ // or mutable global data
+ //
+ // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
+ // and can be marked as both `readonly` and `noalias`, as
+ // LLVM's definition of `noalias` is based solely on memory
+ // dependencies rather than pointer equality
+ //
+ // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
+ // for UniqueBorrowed arguments, so that the codegen backend can decide whether
+ // or not to actually emit the attribute. It can also be controlled with the
+ // `-Zmutable-noalias` debugging option.
+ let no_alias = match kind {
+ PointerKind::SharedMutable
+ | PointerKind::UniqueBorrowed
+ | PointerKind::UniqueBorrowedPinned => false,
+ PointerKind::UniqueOwned => noalias_for_box,
+ PointerKind::Frozen => !is_return,
+ };
+ if no_alias {
+ attrs.set(ArgAttribute::NoAlias);
+ }
+
+ if kind == PointerKind::Frozen && !is_return {
+ attrs.set(ArgAttribute::ReadOnly);
+ }
+
+ if kind == PointerKind::UniqueBorrowed && !is_return {
+ attrs.set(ArgAttribute::NoAliasMutRef);
+ }
+ }
+ }
+}
+
impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
// arguments of this method, into a separate `struct`.
+ #[tracing::instrument(
+ level = "debug",
+ skip(self, caller_location, fn_def_id, force_thin_self_ptr)
+ )]
fn fn_abi_new_uncached(
&self,
sig: ty::PolyFnSig<'tcx>,
@@ -3191,8 +3278,6 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// FIXME(eddyb) replace this with something typed, like an `enum`.
force_thin_self_ptr: bool,
) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
- debug!("fn_abi_new_uncached({:?}, {:?})", sig, extra_args);
-
let sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, sig);
let conv = conv_from_spec_abi(self.tcx(), sig.abi);
@@ -3234,92 +3319,9 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
use SpecAbi::*;
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
- // Handle safe Rust thin and fat pointers.
- let adjust_for_rust_scalar = |attrs: &mut ArgAttributes,
- scalar: Scalar,
- layout: TyAndLayout<'tcx>,
- offset: Size,
- is_return: bool| {
- // Booleans are always a noundef i1 that needs to be zero-extended.
- if scalar.is_bool() {
- attrs.ext(ArgExtension::Zext);
- attrs.set(ArgAttribute::NoUndef);
- return;
- }
-
- // Scalars which have invalid values cannot be undef.
- if !scalar.is_always_valid(self) {
- attrs.set(ArgAttribute::NoUndef);
- }
-
- // Only pointer types handled below.
- let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
-
- if !valid_range.contains(0) {
- attrs.set(ArgAttribute::NonNull);
- }
-
- if let Some(pointee) = layout.pointee_info_at(self, offset) {
- if let Some(kind) = pointee.safe {
- attrs.pointee_align = Some(pointee.align);
-
- // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
- // for the entire duration of the function as they can be deallocated
- // at any time. Same for shared mutable references. If LLVM had a
- // way to say "dereferenceable on entry" we could use it here.
- attrs.pointee_size = match kind {
- PointerKind::UniqueBorrowed
- | PointerKind::UniqueBorrowedPinned
- | PointerKind::Frozen => pointee.size,
- PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
- };
-
- // `Box`, `&T`, and `&mut T` cannot be undef.
- // Note that this only applies to the value of the pointer itself;
- // this attribute doesn't make it UB for the pointed-to data to be undef.
- attrs.set(ArgAttribute::NoUndef);
-
- // The aliasing rules for `Box<T>` are still not decided, but currently we emit
- // `noalias` for it. This can be turned off using an unstable flag.
- // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
- let noalias_for_box =
- self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true);
-
- // `&mut` pointer parameters never alias other parameters,
- // or mutable global data
- //
- // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
- // and can be marked as both `readonly` and `noalias`, as
- // LLVM's definition of `noalias` is based solely on memory
- // dependencies rather than pointer equality
- //
- // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
- // for UniqueBorrowed arguments, so that the codegen backend can decide whether
- // or not to actually emit the attribute. It can also be controlled with the
- // `-Zmutable-noalias` debugging option.
- let no_alias = match kind {
- PointerKind::SharedMutable
- | PointerKind::UniqueBorrowed
- | PointerKind::UniqueBorrowedPinned => false,
- PointerKind::UniqueOwned => noalias_for_box,
- PointerKind::Frozen => !is_return,
- };
- if no_alias {
- attrs.set(ArgAttribute::NoAlias);
- }
-
- if kind == PointerKind::Frozen && !is_return {
- attrs.set(ArgAttribute::ReadOnly);
- }
-
- if kind == PointerKind::UniqueBorrowed && !is_return {
- attrs.set(ArgAttribute::NoAliasMutRef);
- }
- }
- }
- };
-
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
+ let span = tracing::debug_span!("arg_of");
+ let _entered = span.enter();
let is_return = arg_idx.is_none();
let layout = self.layout_of(ty)?;
@@ -3334,7 +3336,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| {
let mut attrs = ArgAttributes::new();
- adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return);
+ adjust_for_rust_scalar(*self, &mut attrs, scalar, *layout, offset, is_return);
attrs
});
@@ -3367,7 +3369,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
.map(|(i, ty)| arg_of(ty, Some(i)))
.collect::<Result<_, _>>()?,
c_variadic: sig.c_variadic,
- fixed_count: inputs.len(),
+ fixed_count: inputs.len() as u32,
conv,
can_unwind: fn_can_unwind(self.tcx(), fn_def_id, sig.abi),
};
@@ -3376,6 +3378,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
Ok(self.tcx.arena.alloc(fn_abi))
}
+ #[tracing::instrument(level = "trace", skip(self))]
fn fn_abi_adjust_for_abi(
&self,
fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>,
@@ -3439,7 +3442,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
}
};
fixup(&mut fn_abi.ret);
- for arg in &mut fn_abi.args {
+ for arg in fn_abi.args.iter_mut() {
fixup(arg);
}
} else {
@@ -3450,6 +3453,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
}
}
+#[tracing::instrument(level = "debug", skip(cx))]
fn make_thin_self_ptr<'tcx>(
cx: &(impl HasTyCtxt<'tcx> + HasParamEnv<'tcx>),
layout: TyAndLayout<'tcx>,
@@ -3461,7 +3465,7 @@ fn make_thin_self_ptr<'tcx>(
tcx.mk_mut_ptr(layout.ty)
} else {
match layout.abi {
- Abi::ScalarPair(..) => (),
+ Abi::ScalarPair(..) | Abi::Scalar(..) => (),
_ => bug!("receiver type has unsupported layout: {:?}", layout),
}