1
Fork 0

layout computation: eagerly error for unexpected unsized fields

This commit is contained in:
Lukas Markeffsky 2024-09-15 22:16:21 +02:00
parent 16be6666d4
commit 697450151c
25 changed files with 1246 additions and 1115 deletions

View file

@ -1,4 +1,3 @@
use std::borrow::{Borrow, Cow};
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::ops::{Bound, Deref}; use std::ops::{Bound, Deref};
use std::{cmp, iter}; use std::{cmp, iter};
@ -7,8 +6,8 @@ use rustc_index::Idx;
use tracing::debug; use tracing::debug;
use crate::{ use crate::{
Abi, AbiAndPrefAlign, Align, FieldsShape, IndexSlice, IndexVec, Integer, LayoutS, Niche, Abi, AbiAndPrefAlign, Align, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer,
NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding, TargetDataLayout, LayoutS, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding,
Variants, WrappingRange, Variants, WrappingRange,
}; };
@ -30,19 +29,46 @@ where
uninhabited && is_1zst uninhabited && is_1zst
} }
pub trait LayoutCalculator { /// Determines towards which end of a struct layout optimizations will try to place the best niches.
type TargetDataLayoutRef: Borrow<TargetDataLayout>; enum NicheBias {
Start,
End,
}
fn delayed_bug(&self, txt: impl Into<Cow<'static, str>>); #[derive(Copy, Clone, Debug)]
fn current_data_layout(&self) -> Self::TargetDataLayoutRef; pub enum LayoutCalculatorError {
/// An unsized type was found in a location where a sized type was expected.
///
/// This is not always a compile error, for example if there is a `[T]: Sized`
/// bound in a where clause.
UnexpectedUnsized,
fn scalar_pair<FieldIdx: Idx, VariantIdx: Idx>( /// A type was too large for the target platform.
SizeOverflow,
/// A union had no fields.
EmptyUnion,
}
type LayoutCalculatorResult<FieldIdx, VariantIdx> =
Result<LayoutS<FieldIdx, VariantIdx>, LayoutCalculatorError>;
#[derive(Clone, Copy, Debug)]
pub struct LayoutCalculator<Cx> {
pub cx: Cx,
}
impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
pub fn new(cx: Cx) -> Self {
Self { cx }
}
pub fn scalar_pair<FieldIdx: Idx, VariantIdx: Idx>(
&self, &self,
a: Scalar, a: Scalar,
b: Scalar, b: Scalar,
) -> LayoutS<FieldIdx, VariantIdx> { ) -> LayoutS<FieldIdx, VariantIdx> {
let dl = self.current_data_layout(); let dl = self.cx.data_layout();
let dl = dl.borrow();
let b_align = b.align(dl); let b_align = b.align(dl);
let align = a.align(dl).max(b_align).max(dl.aggregate_align); let align = a.align(dl).max(b_align).max(dl.aggregate_align);
let b_offset = a.size(dl).align_to(b_align.abi); let b_offset = a.size(dl).align_to(b_align.abi);
@ -70,25 +96,25 @@ pub trait LayoutCalculator {
} }
} }
fn univariant< pub fn univariant<
'a, 'a,
FieldIdx: Idx, FieldIdx: Idx,
VariantIdx: Idx, VariantIdx: Idx,
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug, F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
>( >(
&self, &self,
dl: &TargetDataLayout,
fields: &IndexSlice<FieldIdx, F>, fields: &IndexSlice<FieldIdx, F>,
repr: &ReprOptions, repr: &ReprOptions,
kind: StructKind, kind: StructKind,
) -> Option<LayoutS<FieldIdx, VariantIdx>> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx> {
let layout = univariant(self, dl, fields, repr, kind, NicheBias::Start); let dl = self.cx.data_layout();
let layout = self.univariant_biased(fields, repr, kind, NicheBias::Start);
// Enums prefer niches close to the beginning or the end of the variants so that other // Enums prefer niches close to the beginning or the end of the variants so that other
// (smaller) data-carrying variants can be packed into the space after/before the niche. // (smaller) data-carrying variants can be packed into the space after/before the niche.
// If the default field ordering does not give us a niche at the front then we do a second // If the default field ordering does not give us a niche at the front then we do a second
// run and bias niches to the right and then check which one is closer to one of the // run and bias niches to the right and then check which one is closer to one of the
// struct's edges. // struct's edges.
if let Some(layout) = &layout { if let Ok(layout) = &layout {
// Don't try to calculate an end-biased layout for unsizable structs, // Don't try to calculate an end-biased layout for unsizable structs,
// otherwise we could end up with different layouts for // otherwise we could end up with different layouts for
// Foo<Type> and Foo<dyn Trait> which would break unsizing. // Foo<Type> and Foo<dyn Trait> which would break unsizing.
@ -102,7 +128,8 @@ pub trait LayoutCalculator {
// field (e.g. a trailing bool) and there is tail padding. But it's non-trivial // field (e.g. a trailing bool) and there is tail padding. But it's non-trivial
// to get the unpadded size so we try anyway. // to get the unpadded size so we try anyway.
if fields.len() > 1 && head_space != 0 && tail_space > 0 { if fields.len() > 1 && head_space != 0 && tail_space > 0 {
let alt_layout = univariant(self, dl, fields, repr, kind, NicheBias::End) let alt_layout = self
.univariant_biased(fields, repr, kind, NicheBias::End)
.expect("alt layout should always work"); .expect("alt layout should always work");
let alt_niche = alt_layout let alt_niche = alt_layout
.largest_niche .largest_niche
@ -130,12 +157,12 @@ pub trait LayoutCalculator {
alt_tail_space, alt_tail_space,
layout.fields.count(), layout.fields.count(),
prefer_alt_layout, prefer_alt_layout,
format_field_niches(layout, fields, dl), self.format_field_niches(layout, fields),
format_field_niches(&alt_layout, fields, dl), self.format_field_niches(&alt_layout, fields),
); );
if prefer_alt_layout { if prefer_alt_layout {
return Some(alt_layout); return Ok(alt_layout);
} }
} }
} }
@ -144,11 +171,10 @@ pub trait LayoutCalculator {
layout layout
} }
fn layout_of_never_type<FieldIdx: Idx, VariantIdx: Idx>( pub fn layout_of_never_type<FieldIdx: Idx, VariantIdx: Idx>(
&self, &self,
) -> LayoutS<FieldIdx, VariantIdx> { ) -> LayoutS<FieldIdx, VariantIdx> {
let dl = self.current_data_layout(); let dl = self.cx.data_layout();
let dl = dl.borrow();
LayoutS { LayoutS {
variants: Variants::Single { index: VariantIdx::new(0) }, variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Primitive, fields: FieldsShape::Primitive,
@ -161,7 +187,7 @@ pub trait LayoutCalculator {
} }
} }
fn layout_of_struct_or_enum< pub fn layout_of_struct_or_enum<
'a, 'a,
FieldIdx: Idx, FieldIdx: Idx,
VariantIdx: Idx, VariantIdx: Idx,
@ -177,10 +203,7 @@ pub trait LayoutCalculator {
discriminants: impl Iterator<Item = (VariantIdx, i128)>, discriminants: impl Iterator<Item = (VariantIdx, i128)>,
dont_niche_optimize_enum: bool, dont_niche_optimize_enum: bool,
always_sized: bool, always_sized: bool,
) -> Option<LayoutS<FieldIdx, VariantIdx>> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx> {
let dl = self.current_data_layout();
let dl = dl.borrow();
let (present_first, present_second) = { let (present_first, present_second) = {
let mut present_variants = variants let mut present_variants = variants
.iter_enumerated() .iter_enumerated()
@ -191,7 +214,7 @@ pub trait LayoutCalculator {
Some(present_first) => present_first, Some(present_first) => present_first,
// Uninhabited because it has no variants, or only absent ones. // Uninhabited because it has no variants, or only absent ones.
None if is_enum => { None if is_enum => {
return Some(self.layout_of_never_type()); return Ok(self.layout_of_never_type());
} }
// If it's a struct, still compute a layout so that we can still compute the // If it's a struct, still compute a layout so that we can still compute the
// field offsets. // field offsets.
@ -203,15 +226,13 @@ pub trait LayoutCalculator {
// or for optimizing univariant enums // or for optimizing univariant enums
(present_second.is_none() && !repr.inhibit_enum_layout_opt()) (present_second.is_none() && !repr.inhibit_enum_layout_opt())
{ {
layout_of_struct( self.layout_of_struct(
self,
repr, repr,
variants, variants,
is_enum, is_enum,
is_unsafe_cell, is_unsafe_cell,
scalar_valid_range, scalar_valid_range,
always_sized, always_sized,
dl,
present_first, present_first,
) )
} else { } else {
@ -219,19 +240,17 @@ pub trait LayoutCalculator {
// structs. (We have also handled univariant enums // structs. (We have also handled univariant enums
// that allow representation optimization.) // that allow representation optimization.)
assert!(is_enum); assert!(is_enum);
layout_of_enum( self.layout_of_enum(
self,
repr, repr,
variants, variants,
discr_range_of_repr, discr_range_of_repr,
discriminants, discriminants,
dont_niche_optimize_enum, dont_niche_optimize_enum,
dl,
) )
} }
} }
fn layout_of_union< pub fn layout_of_union<
'a, 'a,
FieldIdx: Idx, FieldIdx: Idx,
VariantIdx: Idx, VariantIdx: Idx,
@ -240,9 +259,8 @@ pub trait LayoutCalculator {
&self, &self,
repr: &ReprOptions, repr: &ReprOptions,
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>, variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
) -> Option<LayoutS<FieldIdx, VariantIdx>> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx> {
let dl = self.current_data_layout(); let dl = self.cx.data_layout();
let dl = dl.borrow();
let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
let mut max_repr_align = repr.align; let mut max_repr_align = repr.align;
@ -257,10 +275,11 @@ pub trait LayoutCalculator {
}; };
let mut size = Size::ZERO; let mut size = Size::ZERO;
let only_variant = &variants[VariantIdx::new(0)]; let only_variant_idx = VariantIdx::new(0);
let only_variant = &variants[only_variant_idx];
for field in only_variant { for field in only_variant {
if field.is_unsized() { if field.is_unsized() {
self.delayed_bug("unsized field in union".to_string()); return Err(LayoutCalculatorError::UnexpectedUnsized);
} }
align = align.max(field.align); align = align.max(field.align);
@ -323,9 +342,13 @@ pub trait LayoutCalculator {
} }
}; };
Some(LayoutS { let Some(union_field_count) = NonZeroUsize::new(only_variant.len()) else {
variants: Variants::Single { index: VariantIdx::new(0) }, return Err(LayoutCalculatorError::EmptyUnion);
fields: FieldsShape::Union(NonZeroUsize::new(only_variant.len())?), };
Ok(LayoutS {
variants: Variants::Single { index: only_variant_idx },
fields: FieldsShape::Union(union_field_count),
abi, abi,
largest_niche: None, largest_niche: None,
align, align,
@ -334,27 +357,25 @@ pub trait LayoutCalculator {
unadjusted_abi_align, unadjusted_abi_align,
}) })
} }
}
/// single-variant enums are just structs, if you think about it /// single-variant enums are just structs, if you think about it
fn layout_of_struct<'a, LC, FieldIdx: Idx, VariantIdx: Idx, F>( fn layout_of_struct<'a, FieldIdx: Idx, VariantIdx: Idx, F>(
layout_calc: &LC, &self,
repr: &ReprOptions, repr: &ReprOptions,
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>, variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
is_enum: bool, is_enum: bool,
is_unsafe_cell: bool, is_unsafe_cell: bool,
scalar_valid_range: (Bound<u128>, Bound<u128>), scalar_valid_range: (Bound<u128>, Bound<u128>),
always_sized: bool, always_sized: bool,
dl: &TargetDataLayout,
present_first: VariantIdx, present_first: VariantIdx,
) -> Option<LayoutS<FieldIdx, VariantIdx>> ) -> LayoutCalculatorResult<FieldIdx, VariantIdx>
where where
LC: LayoutCalculator + ?Sized,
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug, F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
{ {
// Struct, or univariant enum equivalent to a struct. // Struct, or univariant enum equivalent to a struct.
// (Typechecking will reject discriminant-sizing attrs.) // (Typechecking will reject discriminant-sizing attrs.)
let dl = self.cx.data_layout();
let v = present_first; let v = present_first;
let kind = if is_enum || variants[v].is_empty() || always_sized { let kind = if is_enum || variants[v].is_empty() || always_sized {
StructKind::AlwaysSized StructKind::AlwaysSized
@ -362,7 +383,7 @@ where
StructKind::MaybeUnsized StructKind::MaybeUnsized
}; };
let mut st = layout_calc.univariant(dl, &variants[v], repr, kind)?; let mut st = self.univariant(&variants[v], repr, kind)?;
st.variants = Variants::Single { index: v }; st.variants = Variants::Single { index: v };
if is_unsafe_cell { if is_unsafe_cell {
@ -384,7 +405,7 @@ where
Abi::Aggregate { sized: _ } => {} Abi::Aggregate { sized: _ } => {}
} }
st.largest_niche = None; st.largest_niche = None;
return Some(st); return Ok(st);
} }
let (start, end) = scalar_valid_range; let (start, end) = scalar_valid_range;
@ -433,22 +454,20 @@ where
), ),
} }
Some(st) Ok(st)
} }
fn layout_of_enum<'a, LC, FieldIdx: Idx, VariantIdx: Idx, F>( fn layout_of_enum<'a, FieldIdx: Idx, VariantIdx: Idx, F>(
layout_calc: &LC, &self,
repr: &ReprOptions, repr: &ReprOptions,
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>, variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool), discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, i128)>, discriminants: impl Iterator<Item = (VariantIdx, i128)>,
dont_niche_optimize_enum: bool, dont_niche_optimize_enum: bool,
dl: &TargetDataLayout, ) -> LayoutCalculatorResult<FieldIdx, VariantIdx>
) -> Option<LayoutS<FieldIdx, VariantIdx>> where
where
LC: LayoutCalculator + ?Sized,
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug, F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
{ {
// Until we've decided whether to use the tagged or // Until we've decided whether to use the tagged or
// niche filling LayoutS, we don't want to intern the // niche filling LayoutS, we don't want to intern the
// variant layouts, so we can't store them in the // variant layouts, so we can't store them in the
@ -459,6 +478,8 @@ where
variants: IndexVec<VariantIdx, LayoutS<FieldIdx, VariantIdx>>, variants: IndexVec<VariantIdx, LayoutS<FieldIdx, VariantIdx>>,
} }
let dl = self.cx.data_layout();
let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> { let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> {
if dont_niche_optimize_enum { if dont_niche_optimize_enum {
return None; return None;
@ -475,7 +496,7 @@ where
let mut variant_layouts = variants let mut variant_layouts = variants
.iter_enumerated() .iter_enumerated()
.map(|(j, v)| { .map(|(j, v)| {
let mut st = layout_calc.univariant(dl, v, repr, StructKind::AlwaysSized)?; let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?;
st.variants = Variants::Single { index: j }; st.variants = Variants::Single { index: j };
align = align.max(st.align); align = align.max(st.align);
@ -669,8 +690,7 @@ where
let mut layout_variants = variants let mut layout_variants = variants
.iter_enumerated() .iter_enumerated()
.map(|(i, field_layouts)| { .map(|(i, field_layouts)| {
let mut st = layout_calc.univariant( let mut st = self.univariant(
dl,
field_layouts, field_layouts,
repr, repr,
StructKind::Prefixed(min_ity.size(), prefix_align), StructKind::Prefixed(min_ity.size(), prefix_align),
@ -689,16 +709,16 @@ where
align = align.max(st.align); align = align.max(st.align);
max_repr_align = max_repr_align.max(st.max_repr_align); max_repr_align = max_repr_align.max(st.max_repr_align);
unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align); unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align);
Some(st) Ok(st)
}) })
.collect::<Option<IndexVec<VariantIdx, _>>>()?; .collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
// Align the maximum variant size to the largest alignment. // Align the maximum variant size to the largest alignment.
size = size.align_to(align.abi); size = size.align_to(align.abi);
// FIXME(oli-obk): deduplicate and harden these checks // FIXME(oli-obk): deduplicate and harden these checks
if size.bytes() >= dl.obj_size_bound() { if size.bytes() >= dl.obj_size_bound() {
return None; return Err(LayoutCalculatorError::SizeOverflow);
} }
let typeck_ity = Integer::from_attr(dl, repr.discr_type()); let typeck_ity = Integer::from_attr(dl, repr.discr_type());
@ -858,7 +878,7 @@ where
// Common prim might be uninit. // Common prim might be uninit.
Scalar::Union { value: prim } Scalar::Union { value: prim }
}; };
let pair = layout_calc.scalar_pair::<FieldIdx, VariantIdx>(tag, prim_scalar); let pair = self.scalar_pair::<FieldIdx, VariantIdx>(tag, prim_scalar);
let pair_offsets = match pair.fields { let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref memory_index } => { FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index.raw, [0, 1]); assert_eq!(memory_index.raw, [0, 1]);
@ -904,7 +924,10 @@ where
tag_field: 0, tag_field: 0,
variants: IndexVec::new(), variants: IndexVec::new(),
}, },
fields: FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), memory_index: [0].into() }, fields: FieldsShape::Arbitrary {
offsets: [Size::ZERO].into(),
memory_index: [0].into(),
},
largest_niche, largest_niche,
abi, abi,
align, align,
@ -942,35 +965,30 @@ where
panic!("encountered a single-variant enum during multi-variant layout") panic!("encountered a single-variant enum during multi-variant layout")
} }
}; };
Some(best_layout.layout) Ok(best_layout.layout)
} }
/// Determines towards which end of a struct layout optimizations will try to place the best niches. fn univariant_biased<
enum NicheBias {
Start,
End,
}
fn univariant<
'a, 'a,
FieldIdx: Idx, FieldIdx: Idx,
VariantIdx: Idx, VariantIdx: Idx,
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug, F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
>( >(
this: &(impl LayoutCalculator + ?Sized), &self,
dl: &TargetDataLayout,
fields: &IndexSlice<FieldIdx, F>, fields: &IndexSlice<FieldIdx, F>,
repr: &ReprOptions, repr: &ReprOptions,
kind: StructKind, kind: StructKind,
niche_bias: NicheBias, niche_bias: NicheBias,
) -> Option<LayoutS<FieldIdx, VariantIdx>> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx> {
let dl = self.cx.data_layout();
let pack = repr.pack; let pack = repr.pack;
let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
let mut max_repr_align = repr.align; let mut max_repr_align = repr.align;
let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect(); let mut inverse_memory_index: IndexVec<u32, FieldIdx> = fields.indices().collect();
let optimize_field_order = !repr.inhibit_struct_field_reordering(); let optimize_field_order = !repr.inhibit_struct_field_reordering();
if optimize_field_order && fields.len() > 1 { if optimize_field_order && fields.len() > 1 {
let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; let end =
if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() };
let optimizing = &mut inverse_memory_index.raw[..end]; let optimizing = &mut inverse_memory_index.raw[..end];
let fields_excluding_tail = &fields.raw[..end]; let fields_excluding_tail = &fields.raw[..end];
@ -1025,7 +1043,9 @@ fn univariant<
// Given `A(u8, [u8; 16])` and `B(bool, [u8; 16])` we want to bump the // Given `A(u8, [u8; 16])` and `B(bool, [u8; 16])` we want to bump the
// array to the front in the first case (for aligned loads) but keep // array to the front in the first case (for aligned loads) but keep
// the bool in front in the second case for its niches. // the bool in front in the second case for its niches.
NicheBias::Start => max_field_align.trailing_zeros().min(size_as_align), NicheBias::Start => {
max_field_align.trailing_zeros().min(size_as_align)
}
// When moving niches towards the end of the struct then for // When moving niches towards the end of the struct then for
// A((u8, u8, u8, bool), (u8, bool, u8)) we want to keep the first tuple // A((u8, u8, u8, bool), (u8, bool, u8)) we want to keep the first tuple
// in the align-1 group because its bool can be moved closer to the end. // in the align-1 group because its bool can be moved closer to the end.
@ -1118,10 +1138,7 @@ fn univariant<
for &i in &inverse_memory_index { for &i in &inverse_memory_index {
let field = &fields[i]; let field = &fields[i];
if !sized { if !sized {
this.delayed_bug(format!( return Err(LayoutCalculatorError::UnexpectedUnsized);
"univariant: field #{} comes after unsized field",
offsets.len(),
));
} }
if field.is_unsized() { if field.is_unsized() {
@ -1156,7 +1173,8 @@ fn univariant<
} }
} }
offset = offset.checked_add(field.size, dl)?; offset =
offset.checked_add(field.size, dl).ok_or(LayoutCalculatorError::SizeOverflow)?;
} }
// The unadjusted ABI alignment does not include repr(align), but does include repr(pack). // The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
@ -1185,7 +1203,7 @@ fn univariant<
let size = min_size.align_to(align.abi); let size = min_size.align_to(align.abi);
// FIXME(oli-obk): deduplicate and harden these checks // FIXME(oli-obk): deduplicate and harden these checks
if size.bytes() >= dl.obj_size_bound() { if size.bytes() >= dl.obj_size_bound() {
return None; return Err(LayoutCalculatorError::SizeOverflow);
} }
let mut layout_of_single_non_zst_field = None; let mut layout_of_single_non_zst_field = None;
let mut abi = Abi::Aggregate { sized }; let mut abi = Abi::Aggregate { sized };
@ -1204,7 +1222,8 @@ fn univariant<
layout_of_single_non_zst_field = Some(field); layout_of_single_non_zst_field = Some(field);
// Field fills the struct and it has a scalar or scalar pair ABI. // Field fills the struct and it has a scalar or scalar pair ABI.
if offsets[i].bytes() == 0 && align.abi == field.align.abi && size == field.size { if offsets[i].bytes() == 0 && align.abi == field.align.abi && size == field.size
{
match field.abi { match field.abi {
// For plain scalars, or vectors of them, we can't unpack // For plain scalars, or vectors of them, we can't unpack
// newtypes for `#[repr(C)]`, as that affects C ABIs. // newtypes for `#[repr(C)]`, as that affects C ABIs.
@ -1231,7 +1250,7 @@ fn univariant<
} else { } else {
((j, b), (i, a)) ((j, b), (i, a))
}; };
let pair = this.scalar_pair::<FieldIdx, VariantIdx>(a, b); let pair = self.scalar_pair::<FieldIdx, VariantIdx>(a, b);
let pair_offsets = match pair.fields { let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref memory_index } => { FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index.raw, [0, 1]); assert_eq!(memory_index.raw, [0, 1]);
@ -1276,7 +1295,7 @@ fn univariant<
unadjusted_abi_align unadjusted_abi_align
}; };
Some(LayoutS { Ok(LayoutS {
variants: Variants::Single { index: VariantIdx::new(0) }, variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Arbitrary { offsets, memory_index }, fields: FieldsShape::Arbitrary { offsets, memory_index },
abi, abi,
@ -1286,18 +1305,19 @@ fn univariant<
max_repr_align, max_repr_align,
unadjusted_abi_align, unadjusted_abi_align,
}) })
} }
fn format_field_niches< fn format_field_niches<
'a, 'a,
FieldIdx: Idx, FieldIdx: Idx,
VariantIdx: Idx, VariantIdx: Idx,
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug, F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug,
>( >(
&self,
layout: &LayoutS<FieldIdx, VariantIdx>, layout: &LayoutS<FieldIdx, VariantIdx>,
fields: &IndexSlice<FieldIdx, F>, fields: &IndexSlice<FieldIdx, F>,
dl: &TargetDataLayout, ) -> String {
) -> String { let dl = self.cx.data_layout();
let mut s = String::new(); let mut s = String::new();
for i in layout.fields.index_by_increasing_offset() { for i in layout.fields.index_by_increasing_offset() {
let offset = layout.fields.offset(i); let offset = layout.fields.offset(i);
@ -1316,4 +1336,5 @@ fn format_field_niches<
write!(s, "] ").unwrap(); write!(s, "] ").unwrap();
} }
s s
}
} }

View file

@ -26,7 +26,7 @@ mod layout;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use layout::LayoutCalculator; pub use layout::{LayoutCalculator, LayoutCalculatorError};
/// Requirements for a `StableHashingContext` to be used in this crate. /// Requirements for a `StableHashingContext` to be used in this crate.
/// This is a hack to allow using the `HashStable_Generic` derive macro /// This is a hack to allow using the `HashStable_Generic` derive macro
@ -393,6 +393,14 @@ impl HasDataLayout for TargetDataLayout {
} }
} }
// used by rust-analyzer
impl HasDataLayout for &TargetDataLayout {
#[inline]
fn data_layout(&self) -> &TargetDataLayout {
(**self).data_layout()
}
}
/// Endianness of the target, which must match cfg(target-endian). /// Endianness of the target, which must match cfg(target-endian).
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub enum Endian { pub enum Endian {

View file

@ -319,7 +319,7 @@ pub fn valtree_to_const_value<'tcx>(
let branches = valtree.unwrap_branch(); let branches = valtree.unwrap_branch();
// Find the non-ZST field. (There can be aligned ZST!) // Find the non-ZST field. (There can be aligned ZST!)
for (i, &inner_valtree) in branches.iter().enumerate() { for (i, &inner_valtree) in branches.iter().enumerate() {
let field = layout.field(&LayoutCx { tcx, param_env }, i); let field = layout.field(&LayoutCx::new(tcx, param_env), i);
if !field.is_zst() { if !field.is_zst() {
return valtree_to_const_value(tcx, param_env.and(field.ty), inner_valtree); return valtree_to_const_value(tcx, param_env.and(field.ty), inner_valtree);
} }

View file

@ -940,7 +940,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
) -> Cow<'e, RangeSet> { ) -> Cow<'e, RangeSet> {
assert!(layout.ty.is_union()); assert!(layout.ty.is_union());
assert!(layout.abi.is_sized(), "there are no unsized unions"); assert!(layout.abi.is_sized(), "there are no unsized unions");
let layout_cx = LayoutCx { tcx: *ecx.tcx, param_env: ecx.param_env }; let layout_cx = LayoutCx::new(*ecx.tcx, ecx.param_env);
return M::cached_union_data_range(ecx, layout.ty, || { return M::cached_union_data_range(ecx, layout.ty, || {
let mut out = RangeSet(Vec::new()); let mut out = RangeSet(Vec::new());
union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out); union_data_range_uncached(&layout_cx, layout, Size::ZERO, &mut out);

View file

@ -1,5 +1,7 @@
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::layout::{
HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement,
};
use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
@ -30,7 +32,7 @@ pub fn check_validity_requirement<'tcx>(
return Ok(!layout.abi.is_uninhabited()); return Ok(!layout.abi.is_uninhabited());
} }
let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env }; let layout_cx = LayoutCx::new(tcx, param_env_and_ty.param_env);
if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks {
check_validity_requirement_strict(layout, &layout_cx, kind) check_validity_requirement_strict(layout, &layout_cx, kind)
} else { } else {
@ -47,7 +49,7 @@ fn check_validity_requirement_strict<'tcx>(
) -> Result<bool, &'tcx LayoutError<'tcx>> { ) -> Result<bool, &'tcx LayoutError<'tcx>> {
let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
let mut cx = InterpCx::new(cx.tcx, rustc_span::DUMMY_SP, cx.param_env, machine); let mut cx = InterpCx::new(cx.tcx(), rustc_span::DUMMY_SP, cx.param_env, machine);
let allocated = cx let allocated = cx
.allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))

View file

@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::num::NonZero; use std::num::NonZero;
use std::ops::Bound; use std::ops::Bound;
use std::{cmp, fmt}; use std::{cmp, fmt};
@ -287,19 +286,13 @@ impl<'tcx> IntoDiagArg for LayoutError<'tcx> {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct LayoutCx<'tcx> { pub struct LayoutCx<'tcx> {
pub tcx: TyCtxt<'tcx>, pub calc: LayoutCalculator<TyCtxt<'tcx>>,
pub param_env: ty::ParamEnv<'tcx>, pub param_env: ty::ParamEnv<'tcx>,
} }
impl<'tcx> LayoutCalculator for LayoutCx<'tcx> { impl<'tcx> LayoutCx<'tcx> {
type TargetDataLayoutRef = &'tcx TargetDataLayout; pub fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
Self { calc: LayoutCalculator::new(tcx), param_env }
fn delayed_bug(&self, txt: impl Into<Cow<'static, str>>) {
self.tcx.dcx().delayed_bug(txt);
}
fn current_data_layout(&self) -> Self::TargetDataLayoutRef {
&self.tcx.data_layout
} }
} }
@ -576,25 +569,25 @@ impl<'tcx> HasParamEnv<'tcx> for LayoutCx<'tcx> {
impl<'tcx> HasDataLayout for LayoutCx<'tcx> { impl<'tcx> HasDataLayout for LayoutCx<'tcx> {
fn data_layout(&self) -> &TargetDataLayout { fn data_layout(&self) -> &TargetDataLayout {
self.tcx.data_layout() self.calc.cx.data_layout()
} }
} }
impl<'tcx> HasTargetSpec for LayoutCx<'tcx> { impl<'tcx> HasTargetSpec for LayoutCx<'tcx> {
fn target_spec(&self) -> &Target { fn target_spec(&self) -> &Target {
self.tcx.target_spec() self.calc.cx.target_spec()
} }
} }
impl<'tcx> HasWasmCAbiOpt for LayoutCx<'tcx> { impl<'tcx> HasWasmCAbiOpt for LayoutCx<'tcx> {
fn wasm_c_abi_opt(&self) -> WasmCAbi { fn wasm_c_abi_opt(&self) -> WasmCAbi {
self.tcx.wasm_c_abi_opt() self.calc.cx.wasm_c_abi_opt()
} }
} }
impl<'tcx> HasTyCtxt<'tcx> for LayoutCx<'tcx> { impl<'tcx> HasTyCtxt<'tcx> for LayoutCx<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx.tcx() self.calc.cx
} }
} }
@ -695,7 +688,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx> {
_: Span, _: Span,
_: Ty<'tcx>, _: Ty<'tcx>,
) -> &'tcx LayoutError<'tcx> { ) -> &'tcx LayoutError<'tcx> {
self.tcx.arena.alloc(err) self.tcx().arena.alloc(err)
} }
} }
@ -1323,7 +1316,7 @@ impl<'tcx> TyCtxt<'tcx> {
where where
I: Iterator<Item = (VariantIdx, FieldIdx)>, I: Iterator<Item = (VariantIdx, FieldIdx)>,
{ {
let cx = LayoutCx { tcx: self, param_env }; let cx = LayoutCx::new(self, param_env);
let mut offset = Size::ZERO; let mut offset = Size::ZERO;
for (variant, field) in indices { for (variant, field) in indices {

View file

@ -128,7 +128,7 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
} }
Err(layout_error) => { Err(layout_error) => {
tcx.dcx().emit_fatal(Spanned { node: layout_error.into_diagnostic(), span }); tcx.dcx().emit_err(Spanned { node: layout_error.into_diagnostic(), span });
} }
} }
} }

View file

@ -63,7 +63,7 @@ pub mod rustc {
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use rustc_middle::mir::Mutability; use rustc_middle::mir::Mutability;
use rustc_middle::ty::layout::{LayoutCx, LayoutError}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError};
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_target::abi::Layout; use rustc_target::abi::Layout;
@ -128,7 +128,7 @@ pub mod rustc {
ty: Ty<'tcx>, ty: Ty<'tcx>,
) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> { ) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
let ty = cx.tcx.erase_regions(ty); let ty = cx.tcx().erase_regions(ty);
cx.layout_of(ty).map(|tl| tl.layout) cx.layout_of(ty).map(|tl| tl.layout)
} }
} }

View file

@ -212,7 +212,7 @@ pub(crate) mod rustc {
return Err(Err::TypeError(e)); return Err(Err::TypeError(e));
} }
let target = cx.tcx.data_layout(); let target = cx.data_layout();
let pointer_size = target.pointer_size; let pointer_size = target.pointer_size;
match ty.kind() { match ty.kind() {
@ -320,7 +320,7 @@ pub(crate) mod rustc {
// Computes the variant of a given index. // Computes the variant of a given index.
let layout_of_variant = |index, encoding: Option<TagEncoding<VariantIdx>>| { let layout_of_variant = |index, encoding: Option<TagEncoding<VariantIdx>>| {
let tag = cx.tcx.tag_for_variant((cx.tcx.erase_regions(ty), index)); let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index));
let variant_def = Def::Variant(def.variant(index)); let variant_def = Def::Variant(def.variant(index));
let variant_layout = ty_variant(cx, (ty, layout), index); let variant_layout = ty_variant(cx, (ty, layout), index);
Self::from_variant( Self::from_variant(
@ -417,7 +417,7 @@ pub(crate) mod rustc {
} }
} }
} }
struct_tree = struct_tree.then(Self::from_tag(*tag, cx.tcx)); struct_tree = struct_tree.then(Self::from_tag(*tag, cx.tcx()));
} }
// Append the fields, in memory order, to the layout. // Append the fields, in memory order, to the layout.
@ -509,12 +509,12 @@ pub(crate) mod rustc {
match layout.variants { match layout.variants {
Variants::Single { index } => { Variants::Single { index } => {
let field = &def.variant(index).fields[i]; let field = &def.variant(index).fields[i];
field.ty(cx.tcx, args) field.ty(cx.tcx(), args)
} }
// Discriminant field for enums (where applicable). // Discriminant field for enums (where applicable).
Variants::Multiple { tag, .. } => { Variants::Multiple { tag, .. } => {
assert_eq!(i.as_usize(), 0); assert_eq!(i.as_usize(), 0);
ty::layout::PrimitiveExt::to_ty(&tag.primitive(), cx.tcx) ty::layout::PrimitiveExt::to_ty(&tag.primitive(), cx.tcx())
} }
} }
} }
@ -531,7 +531,7 @@ pub(crate) mod rustc {
(ty, layout): (Ty<'tcx>, Layout<'tcx>), (ty, layout): (Ty<'tcx>, Layout<'tcx>),
i: VariantIdx, i: VariantIdx,
) -> Layout<'tcx> { ) -> Layout<'tcx> {
let ty = cx.tcx.erase_regions(ty); let ty = cx.tcx().erase_regions(ty);
TyAndLayout { ty, layout }.for_variant(&cx, i).layout TyAndLayout { ty, layout }.for_variant(&cx, i).layout
} }
} }

View file

@ -43,7 +43,7 @@ mod rustc {
pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> { pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
let Self { src, dst, assume, context } = self; let Self { src, dst, assume, context } = self;
let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() }; let layout_cx = LayoutCx::new(context, ParamEnv::reveal_all());
// Convert `src` and `dst` from their rustc representations, to `Tree`-based // Convert `src` and `dst` from their rustc representations, to `Tree`-based
// representations. // representations.

View file

@ -331,7 +331,7 @@ fn fn_abi_of_fn_ptr<'tcx>(
) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> {
let (param_env, (sig, extra_args)) = query.into_parts(); let (param_env, (sig, extra_args)) = query.into_parts();
let cx = LayoutCx { tcx, param_env }; let cx = LayoutCx::new(tcx, param_env);
fn_abi_new_uncached(&cx, sig, extra_args, None, None, false) fn_abi_new_uncached(&cx, sig, extra_args, None, None, false)
} }
@ -347,7 +347,7 @@ fn fn_abi_of_instance<'tcx>(
instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()); instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty());
fn_abi_new_uncached( fn_abi_new_uncached(
&LayoutCx { tcx, param_env }, &LayoutCx::new(tcx, param_env),
sig, sig,
extra_args, extra_args,
caller_location, caller_location,
@ -386,12 +386,14 @@ fn adjust_for_rust_scalar<'tcx>(
attrs.set(ArgAttribute::NonNull); attrs.set(ArgAttribute::NonNull);
} }
let tcx = cx.tcx();
if let Some(pointee) = layout.pointee_info_at(&cx, offset) { if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
let kind = if let Some(kind) = pointee.safe { let kind = if let Some(kind) = pointee.safe {
Some(kind) Some(kind)
} else if let Some(pointee) = drop_target_pointee { } else if let Some(pointee) = drop_target_pointee {
// The argument to `drop_in_place` is semantically equivalent to a mutable reference. // The argument to `drop_in_place` is semantically equivalent to a mutable reference.
Some(PointerKind::MutableRef { unpin: pointee.is_unpin(cx.tcx, cx.param_env()) }) Some(PointerKind::MutableRef { unpin: pointee.is_unpin(tcx, cx.param_env()) })
} else { } else {
None None
}; };
@ -415,12 +417,12 @@ fn adjust_for_rust_scalar<'tcx>(
// The aliasing rules for `Box<T>` are still not decided, but currently we emit // 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. // `noalias` for it. This can be turned off using an unstable flag.
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias; let noalias_for_box = tcx.sess.opts.unstable_opts.box_noalias;
// LLVM prior to version 12 had known miscompiles in the presence of noalias attributes // LLVM prior to version 12 had known miscompiles in the presence of noalias attributes
// (see #54878), so it was conditionally disabled, but we don't support earlier // (see #54878), so it was conditionally disabled, but we don't support earlier
// versions at all anymore. We still support turning it off using -Zmutable-noalias. // versions at all anymore. We still support turning it off using -Zmutable-noalias.
let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias; let noalias_mut_ref = tcx.sess.opts.unstable_opts.mutable_noalias;
// `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both // `&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 // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on memory
@ -458,6 +460,7 @@ fn fn_abi_sanity_check<'tcx>(
spec_abi: SpecAbi, spec_abi: SpecAbi,
arg: &ArgAbi<'tcx, Ty<'tcx>>, arg: &ArgAbi<'tcx, Ty<'tcx>>,
) { ) {
let tcx = cx.tcx();
match &arg.mode { match &arg.mode {
PassMode::Ignore => {} PassMode::Ignore => {}
PassMode::Direct(_) => { PassMode::Direct(_) => {
@ -484,7 +487,7 @@ fn fn_abi_sanity_check<'tcx>(
// It needs to switch to something else before stabilization can happen. // It needs to switch to something else before stabilization can happen.
// (See issue: https://github.com/rust-lang/rust/issues/117271) // (See issue: https://github.com/rust-lang/rust/issues/117271)
assert!( assert!(
matches!(&*cx.tcx.sess.target.arch, "wasm32" | "wasm64") matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64")
|| matches!(spec_abi, SpecAbi::PtxKernel | SpecAbi::Unadjusted), || matches!(spec_abi, SpecAbi::PtxKernel | SpecAbi::Unadjusted),
"`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\
Problematic type: {:#?}", Problematic type: {:#?}",
@ -516,7 +519,7 @@ fn fn_abi_sanity_check<'tcx>(
// With metadata. Must be unsized and not on the stack. // With metadata. Must be unsized and not on the stack.
assert!(arg.layout.is_unsized() && !on_stack); assert!(arg.layout.is_unsized() && !on_stack);
// Also, must not be `extern` type. // Also, must not be `extern` type.
let tail = cx.tcx.struct_tail_for_codegen(arg.layout.ty, cx.param_env()); let tail = tcx.struct_tail_for_codegen(arg.layout.ty, cx.param_env());
if matches!(tail.kind(), ty::Foreign(..)) { if matches!(tail.kind(), ty::Foreign(..)) {
// These types do not have metadata, so having `meta_attrs` is bogus. // These types do not have metadata, so having `meta_attrs` is bogus.
// Conceptually, unsized arguments must be copied around, which requires dynamically // Conceptually, unsized arguments must be copied around, which requires dynamically
@ -546,7 +549,8 @@ fn fn_abi_new_uncached<'tcx>(
// FIXME(eddyb) replace this with something typed, like an `enum`. // FIXME(eddyb) replace this with something typed, like an `enum`.
force_thin_self_ptr: bool, force_thin_self_ptr: bool,
) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> {
let sig = cx.tcx.normalize_erasing_late_bound_regions(cx.param_env, sig); let tcx = cx.tcx();
let sig = tcx.normalize_erasing_late_bound_regions(cx.param_env, sig);
let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic); let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic);
@ -576,7 +580,7 @@ fn fn_abi_new_uncached<'tcx>(
}; };
let is_drop_in_place = let is_drop_in_place =
fn_def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::DropInPlace)); fn_def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::DropInPlace));
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, &'tcx FnAbiError<'tcx>> { let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, &'tcx FnAbiError<'tcx>> {
let span = tracing::debug_span!("arg_of"); let span = tracing::debug_span!("arg_of");
@ -588,8 +592,7 @@ fn fn_abi_new_uncached<'tcx>(
_ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty), _ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty),
}); });
let layout = let layout = cx.layout_of(ty).map_err(|err| &*tcx.arena.alloc(FnAbiError::Layout(*err)))?;
cx.layout_of(ty).map_err(|err| &*cx.tcx.arena.alloc(FnAbiError::Layout(*err)))?;
let layout = if force_thin_self_ptr && arg_idx == Some(0) { let layout = if force_thin_self_ptr && arg_idx == Some(0) {
// Don't pass the vtable, it's not an argument of the virtual fn. // Don't pass the vtable, it's not an argument of the virtual fn.
// Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
@ -638,7 +641,7 @@ fn fn_abi_new_uncached<'tcx>(
fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id)?; fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id)?;
debug!("fn_abi_new_uncached = {:?}", fn_abi); debug!("fn_abi_new_uncached = {:?}", fn_abi);
fn_abi_sanity_check(cx, &fn_abi, sig.abi); fn_abi_sanity_check(cx, &fn_abi, sig.abi);
Ok(cx.tcx.arena.alloc(fn_abi)) Ok(tcx.arena.alloc(fn_abi))
} }
#[tracing::instrument(level = "trace", skip(cx))] #[tracing::instrument(level = "trace", skip(cx))]
@ -670,14 +673,15 @@ fn fn_abi_adjust_for_abi<'tcx>(
return Ok(()); return Ok(());
} }
let tcx = cx.tcx();
if abi == SpecAbi::Rust || abi == SpecAbi::RustCall || abi == SpecAbi::RustIntrinsic { if abi == SpecAbi::Rust || abi == SpecAbi::RustCall || abi == SpecAbi::RustIntrinsic {
// Look up the deduced parameter attributes for this function, if we have its def ID and // Look up the deduced parameter attributes for this function, if we have its def ID and
// we're optimizing in non-incremental mode. We'll tag its parameters with those attributes // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes
// as appropriate. // as appropriate.
let deduced_param_attrs = if cx.tcx.sess.opts.optimize != OptLevel::No let deduced_param_attrs =
&& cx.tcx.sess.opts.incremental.is_none() if tcx.sess.opts.optimize != OptLevel::No && tcx.sess.opts.incremental.is_none() {
{ fn_def_id.map(|fn_def_id| tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default()
fn_def_id.map(|fn_def_id| cx.tcx.deduced_param_attrs(fn_def_id)).unwrap_or_default()
} else { } else {
&[] &[]
}; };
@ -689,7 +693,7 @@ fn fn_abi_adjust_for_abi<'tcx>(
// Avoid returning floats in x87 registers on x86 as loading and storing from x87 // Avoid returning floats in x87 registers on x86 as loading and storing from x87
// registers will quiet signalling NaNs. // registers will quiet signalling NaNs.
if cx.tcx.sess.target.arch == "x86" if tcx.sess.target.arch == "x86"
&& arg_idx.is_none() && arg_idx.is_none()
// Intrinsics themselves are not actual "real" functions, so theres no need to // Intrinsics themselves are not actual "real" functions, so theres no need to
// change their ABIs. // change their ABIs.
@ -744,7 +748,7 @@ fn fn_abi_adjust_for_abi<'tcx>(
// that's how we connect up to LLVM and it's unstable // that's how we connect up to LLVM and it's unstable
// anyway, we control all calls to it in libstd. // anyway, we control all calls to it in libstd.
Abi::Vector { .. } Abi::Vector { .. }
if abi != SpecAbi::RustIntrinsic && cx.tcx.sess.target.simd_types_indirect => if abi != SpecAbi::RustIntrinsic && tcx.sess.target.simd_types_indirect =>
{ {
arg.make_indirect(); arg.make_indirect();
return; return;
@ -793,7 +797,7 @@ fn fn_abi_adjust_for_abi<'tcx>(
} else { } else {
fn_abi fn_abi
.adjust_for_foreign_abi(cx, abi) .adjust_for_foreign_abi(cx, abi)
.map_err(|err| &*cx.tcx.arena.alloc(FnAbiError::AdjustForForeignAbi(err)))?; .map_err(|err| &*tcx.arena.alloc(FnAbiError::AdjustForForeignAbi(err)))?;
} }
Ok(()) Ok(())

View file

@ -9,7 +9,7 @@ use rustc_middle::bug;
use rustc_middle::mir::{CoroutineLayout, CoroutineSavedLocal}; use rustc_middle::mir::{CoroutineLayout, CoroutineSavedLocal};
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::ty::layout::{ use rustc_middle::ty::layout::{
FloatExt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
}; };
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{ use rustc_middle::ty::{
@ -63,14 +63,14 @@ fn layout_of<'tcx>(
return tcx.layout_of(param_env.and(ty)); return tcx.layout_of(param_env.and(ty));
} }
let cx = LayoutCx { tcx, param_env }; let cx = LayoutCx::new(tcx, param_env);
let layout = layout_of_uncached(&cx, ty)?; let layout = layout_of_uncached(&cx, ty)?;
let layout = TyAndLayout { ty, layout }; let layout = TyAndLayout { ty, layout };
// If we are running with `-Zprint-type-sizes`, maybe record layouts // If we are running with `-Zprint-type-sizes`, maybe record layouts
// for dumping later. // for dumping later.
if cx.tcx.sess.opts.unstable_opts.print_type_sizes { if cx.tcx().sess.opts.unstable_opts.print_type_sizes {
record_layout_for_printing(&cx, layout); record_layout_for_printing(&cx, layout);
} }
@ -80,7 +80,36 @@ fn layout_of<'tcx>(
} }
fn error<'tcx>(cx: &LayoutCx<'tcx>, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> { fn error<'tcx>(cx: &LayoutCx<'tcx>, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> {
cx.tcx.arena.alloc(err) cx.tcx().arena.alloc(err)
}
fn map_error<'tcx>(
cx: &LayoutCx<'tcx>,
ty: Ty<'tcx>,
err: LayoutCalculatorError,
) -> &'tcx LayoutError<'tcx> {
let err = match err {
LayoutCalculatorError::SizeOverflow => {
// This is sometimes not a compile error in `check` builds.
LayoutError::SizeOverflow(ty)
}
LayoutCalculatorError::UnexpectedUnsized => {
// This is sometimes not a compile error if there are trivially false where
// clauses, but it is always a compiler error in the empty environment.
if cx.param_env.caller_bounds().is_empty() {
cx.tcx().dcx().delayed_bug(format!(
"encountered unexpected unsized field in layout of {ty:?}"
));
}
LayoutError::Unknown(ty)
}
LayoutCalculatorError::EmptyUnion => {
// This is always a compile error.
cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}"));
LayoutError::Unknown(ty)
}
};
error(cx, err)
} }
fn univariant_uninterned<'tcx>( fn univariant_uninterned<'tcx>(
@ -90,13 +119,12 @@ fn univariant_uninterned<'tcx>(
repr: &ReprOptions, repr: &ReprOptions,
kind: StructKind, kind: StructKind,
) -> Result<LayoutS<FieldIdx, VariantIdx>, &'tcx LayoutError<'tcx>> { ) -> Result<LayoutS<FieldIdx, VariantIdx>, &'tcx LayoutError<'tcx>> {
let dl = cx.data_layout();
let pack = repr.pack; let pack = repr.pack;
if pack.is_some() && repr.align.is_some() { if pack.is_some() && repr.align.is_some() {
cx.tcx.dcx().bug("struct cannot be packed and aligned"); cx.tcx().dcx().bug("struct cannot be packed and aligned");
} }
cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty))) cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err))
} }
fn layout_of_uncached<'tcx>( fn layout_of_uncached<'tcx>(
@ -110,7 +138,7 @@ fn layout_of_uncached<'tcx>(
return Err(error(cx, LayoutError::ReferencesError(guar))); return Err(error(cx, LayoutError::ReferencesError(guar)));
} }
let tcx = cx.tcx; let tcx = cx.tcx();
let param_env = cx.param_env; let param_env = cx.param_env;
let dl = cx.data_layout(); let dl = cx.data_layout();
let scalar_unit = |value: Primitive| { let scalar_unit = |value: Primitive| {
@ -188,7 +216,7 @@ fn layout_of_uncached<'tcx>(
} }
// The never type. // The never type.
ty::Never => tcx.mk_layout(cx.layout_of_never_type()), ty::Never => tcx.mk_layout(cx.calc.layout_of_never_type()),
// Potentially-wide pointers. // Potentially-wide pointers.
ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => {
@ -264,7 +292,7 @@ fn layout_of_uncached<'tcx>(
}; };
// Effectively a (ptr, meta) tuple. // Effectively a (ptr, meta) tuple.
tcx.mk_layout(cx.scalar_pair(data_ptr, metadata)) tcx.mk_layout(cx.calc.scalar_pair(data_ptr, metadata))
} }
ty::Dynamic(_, _, ty::DynStar) => { ty::Dynamic(_, _, ty::DynStar) => {
@ -272,7 +300,7 @@ fn layout_of_uncached<'tcx>(
data.valid_range_mut().start = 0; data.valid_range_mut().start = 0;
let mut vtable = scalar_unit(Pointer(AddressSpace::DATA)); let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
vtable.valid_range_mut().start = 1; vtable.valid_range_mut().start = 1;
tcx.mk_layout(cx.scalar_pair(data, vtable)) tcx.mk_layout(cx.calc.scalar_pair(data, vtable))
} }
// Arrays and slices. // Arrays and slices.
@ -531,7 +559,7 @@ fn layout_of_uncached<'tcx>(
if def.is_union() { if def.is_union() {
if def.repr().pack.is_some() && def.repr().align.is_some() { if def.repr().pack.is_some() && def.repr().align.is_some() {
cx.tcx.dcx().span_delayed_bug( tcx.dcx().span_delayed_bug(
tcx.def_span(def.did()), tcx.def_span(def.did()),
"union cannot be packed and aligned", "union cannot be packed and aligned",
); );
@ -539,8 +567,9 @@ fn layout_of_uncached<'tcx>(
} }
return Ok(tcx.mk_layout( return Ok(tcx.mk_layout(
cx.layout_of_union(&def.repr(), &variants) cx.calc
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?, .layout_of_union(&def.repr(), &variants)
.map_err(|err| map_error(cx, ty, err))?,
)); ));
} }
@ -557,7 +586,7 @@ fn layout_of_uncached<'tcx>(
})?; })?;
if is_unsized { if is_unsized {
cx.tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned()); tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned());
Err(error(cx, LayoutError::Unknown(ty))) Err(error(cx, LayoutError::Unknown(ty)))
} else { } else {
Ok(()) Ok(())
@ -600,7 +629,9 @@ fn layout_of_uncached<'tcx>(
!tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, param_env) !tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, param_env)
}); });
let Some(layout) = cx.layout_of_struct_or_enum( let layout = cx
.calc
.layout_of_struct_or_enum(
&def.repr(), &def.repr(),
&variants, &variants,
def.is_enum(), def.is_enum(),
@ -610,9 +641,8 @@ fn layout_of_uncached<'tcx>(
discriminants_iter(), discriminants_iter(),
dont_niche_optimize_enum, dont_niche_optimize_enum,
!maybe_unsized, !maybe_unsized,
) else { )
return Err(error(cx, LayoutError::SizeOverflow(ty))); .map_err(|err| map_error(cx, ty, err))?;
};
// If the struct tail is sized and can be unsized, check that unsizing doesn't move the fields around. // If the struct tail is sized and can be unsized, check that unsizing doesn't move the fields around.
if cfg!(debug_assertions) if cfg!(debug_assertions)
@ -623,7 +653,7 @@ fn layout_of_uncached<'tcx>(
let tail_replacement = cx.layout_of(Ty::new_slice(tcx, tcx.types.u8)).unwrap(); let tail_replacement = cx.layout_of(Ty::new_slice(tcx, tcx.types.u8)).unwrap();
*variants[FIRST_VARIANT].raw.last_mut().unwrap() = tail_replacement.layout; *variants[FIRST_VARIANT].raw.last_mut().unwrap() = tail_replacement.layout;
let Some(unsized_layout) = cx.layout_of_struct_or_enum( let Ok(unsized_layout) = cx.calc.layout_of_struct_or_enum(
&def.repr(), &def.repr(),
&variants, &variants,
def.is_enum(), def.is_enum(),
@ -812,7 +842,7 @@ fn coroutine_layout<'tcx>(
args: GenericArgsRef<'tcx>, args: GenericArgsRef<'tcx>,
) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> { ) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
use SavedLocalEligibility::*; use SavedLocalEligibility::*;
let tcx = cx.tcx; let tcx = cx.tcx();
let instantiate_field = |ty: Ty<'tcx>| EarlyBinder::bind(ty).instantiate(tcx, args); let instantiate_field = |ty: Ty<'tcx>| EarlyBinder::bind(ty).instantiate(tcx, args);
let Some(info) = tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) else { let Some(info) = tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) else {
@ -832,7 +862,7 @@ fn coroutine_layout<'tcx>(
value: Primitive::Int(discr_int, false), value: Primitive::Int(discr_int, false),
valid_range: WrappingRange { start: 0, end: max_discr }, valid_range: WrappingRange { start: 0, end: max_discr },
}; };
let tag_layout = cx.tcx.mk_layout(LayoutS::scalar(cx, tag)); let tag_layout = tcx.mk_layout(LayoutS::scalar(cx, tag));
let promoted_layouts = ineligible_locals.iter().map(|local| { let promoted_layouts = ineligible_locals.iter().map(|local| {
let field_ty = instantiate_field(info.field_tys[local].ty); let field_ty = instantiate_field(info.field_tys[local].ty);
@ -1025,7 +1055,7 @@ fn record_layout_for_printing<'tcx>(cx: &LayoutCx<'tcx>, layout: TyAndLayout<'tc
// (delay format until we actually need it) // (delay format until we actually need it)
let record = |kind, packed, opt_discr_size, variants| { let record = |kind, packed, opt_discr_size, variants| {
let type_desc = with_no_trimmed_paths!(format!("{}", layout.ty)); let type_desc = with_no_trimmed_paths!(format!("{}", layout.ty));
cx.tcx.sess.code_stats.record_type_size( cx.tcx().sess.code_stats.record_type_size(
kind, kind,
type_desc, type_desc,
layout.align.abi, layout.align.abi,
@ -1148,8 +1178,8 @@ fn variant_info_for_coroutine<'tcx>(
return (vec![], None); return (vec![], None);
}; };
let coroutine = cx.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap(); let coroutine = cx.tcx().coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap();
let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); let upvar_names = cx.tcx().closure_saved_names_of_captured_variables(def_id);
let mut upvars_size = Size::ZERO; let mut upvars_size = Size::ZERO;
let upvar_fields: Vec<_> = args let upvar_fields: Vec<_> = args

View file

@ -1,20 +1,22 @@
use std::assert_matches::assert_matches; use std::assert_matches::assert_matches;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout};
use rustc_target::abi::*; use rustc_target::abi::*;
/// Enforce some basic invariants on layouts. /// Enforce some basic invariants on layouts.
pub(super) fn sanity_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { pub(super) fn sanity_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) {
let tcx = cx.tcx();
// Type-level uninhabitedness should always imply ABI uninhabitedness. // Type-level uninhabitedness should always imply ABI uninhabitedness.
if layout.ty.is_privately_uninhabited(cx.tcx, cx.param_env) { if layout.ty.is_privately_uninhabited(tcx, cx.param_env) {
assert!(layout.abi.is_uninhabited()); assert!(layout.abi.is_uninhabited());
} }
if layout.size.bytes() % layout.align.abi.bytes() != 0 { if layout.size.bytes() % layout.align.abi.bytes() != 0 {
bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); bug!("size is not a multiple of align, in the following layout:\n{layout:#?}");
} }
if layout.size.bytes() >= cx.tcx.data_layout.obj_size_bound() { if layout.size.bytes() >= tcx.data_layout.obj_size_bound() {
bug!("size is too large, in the following layout:\n{layout:#?}"); bug!("size is too large, in the following layout:\n{layout:#?}");
} }

View file

@ -277,7 +277,7 @@ pub fn create_ecx<'tcx>(
config: &MiriConfig, config: &MiriConfig,
) -> InterpResult<'tcx, InterpCx<'tcx, MiriMachine<'tcx>>> { ) -> InterpResult<'tcx, InterpCx<'tcx, MiriMachine<'tcx>>> {
let param_env = ty::ParamEnv::reveal_all(); let param_env = ty::ParamEnv::reveal_all();
let layout_cx = LayoutCx { tcx, param_env }; let layout_cx = LayoutCx::new(tcx, param_env);
let mut ecx = let mut ecx =
InterpCx::new(tcx, rustc_span::DUMMY_SP, param_env, MiriMachine::new(config, layout_cx)); InterpCx::new(tcx, rustc_span::DUMMY_SP, param_env, MiriMachine::new(config, layout_cx));

View file

@ -21,7 +21,7 @@ use rustc_middle::{
query::TyCtxtAt, query::TyCtxtAt,
ty::{ ty::{
self, self,
layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout}, layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout},
Instance, Ty, TyCtxt, Instance, Ty, TyCtxt,
}, },
}; };
@ -382,7 +382,7 @@ pub struct PrimitiveLayouts<'tcx> {
impl<'tcx> PrimitiveLayouts<'tcx> { impl<'tcx> PrimitiveLayouts<'tcx> {
fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> { fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
let tcx = layout_cx.tcx; let tcx = layout_cx.tcx();
let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit); let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit);
let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
Ok(Self { Ok(Self {
@ -597,13 +597,12 @@ pub struct MiriMachine<'tcx> {
impl<'tcx> MiriMachine<'tcx> { impl<'tcx> MiriMachine<'tcx> {
pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx>) -> Self { pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx>) -> Self {
let tcx = layout_cx.tcx; let tcx = layout_cx.tcx();
let local_crates = helpers::get_local_crates(tcx); let local_crates = helpers::get_local_crates(tcx);
let layouts = let layouts =
PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types"); PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
let profiler = config.measureme_out.as_ref().map(|out| { let profiler = config.measureme_out.as_ref().map(|out| {
let crate_name = layout_cx let crate_name = tcx
.tcx
.sess .sess
.opts .opts
.crate_name .crate_name
@ -701,7 +700,7 @@ impl<'tcx> MiriMachine<'tcx> {
clock: Clock::new(config.isolated_op == IsolatedOp::Allow), clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
#[cfg(unix)] #[cfg(unix)]
native_lib: config.native_lib.as_ref().map(|lib_file_path| { native_lib: config.native_lib.as_ref().map(|lib_file_path| {
let target_triple = layout_cx.tcx.sess.opts.target_triple.triple(); let target_triple = tcx.sess.opts.target_triple.triple();
// Check if host target == the session target. // Check if host target == the session target.
if env!("TARGET") != target_triple { if env!("TARGET") != target_triple {
panic!( panic!(

View file

@ -1,13 +1,13 @@
//! Compute the binary representation of a type //! Compute the binary representation of a type
use std::{borrow::Cow, fmt}; use std::fmt;
use base_db::salsa::Cycle; use base_db::salsa::Cycle;
use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
use hir_def::{ use hir_def::{
layout::{ layout::{
Abi, FieldsShape, Float, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Abi, FieldsShape, Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutS,
Scalar, Size, StructKind, TargetDataLayout, WrappingRange, Primitive, ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange,
}, },
LocalFieldId, StructId, LocalFieldId, StructId,
}; };
@ -15,7 +15,6 @@ use la_arena::{Idx, RawIdx};
use rustc_abi::AddressSpace; use rustc_abi::AddressSpace;
use rustc_index::{IndexSlice, IndexVec}; use rustc_index::{IndexSlice, IndexVec};
use stdx::never;
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{
@ -107,19 +106,24 @@ impl fmt::Display for LayoutError {
} }
} }
struct LayoutCx<'a> { impl From<LayoutCalculatorError> for LayoutError {
target: &'a TargetDataLayout, fn from(err: LayoutCalculatorError) -> Self {
match err {
LayoutCalculatorError::UnexpectedUnsized | LayoutCalculatorError::EmptyUnion => {
LayoutError::Unknown
}
LayoutCalculatorError::SizeOverflow => LayoutError::SizeOverflow,
}
}
} }
impl<'a> LayoutCalculator for LayoutCx<'a> { struct LayoutCx<'a> {
type TargetDataLayoutRef = &'a TargetDataLayout; calc: LayoutCalculator<&'a TargetDataLayout>,
}
fn delayed_bug(&self, txt: impl Into<Cow<'static, str>>) { impl<'a> LayoutCx<'a> {
never!("{}", txt.into()); fn new(target: &'a TargetDataLayout) -> Self {
} Self { calc: LayoutCalculator::new(target) }
fn current_data_layout(&self) -> &'a TargetDataLayout {
self.target
} }
} }
@ -205,8 +209,8 @@ pub fn layout_of_ty_query(
let Ok(target) = db.target_data_layout(krate) else { let Ok(target) = db.target_data_layout(krate) else {
return Err(LayoutError::TargetLayoutNotAvailable); return Err(LayoutError::TargetLayoutNotAvailable);
}; };
let cx = LayoutCx { target: &target }; let dl = &*target;
let dl = cx.current_data_layout(); let cx = LayoutCx::new(dl);
let ty = normalize(db, trait_env.clone(), ty); let ty = normalize(db, trait_env.clone(), ty);
let result = match ty.kind(Interner) { let result = match ty.kind(Interner) {
TyKind::Adt(AdtId(def), subst) => { TyKind::Adt(AdtId(def), subst) => {
@ -281,7 +285,7 @@ pub fn layout_of_ty_query(
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
let fields = fields.iter().collect::<IndexVec<_, _>>(); let fields = fields.iter().collect::<IndexVec<_, _>>();
cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? cx.calc.univariant(&fields, &ReprOptions::default(), kind)?
} }
TyKind::Array(element, count) => { TyKind::Array(element, count) => {
let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
@ -367,12 +371,12 @@ pub fn layout_of_ty_query(
}; };
// Effectively a (ptr, meta) tuple. // Effectively a (ptr, meta) tuple.
cx.scalar_pair(data_ptr, metadata) cx.calc.scalar_pair(data_ptr, metadata)
} }
TyKind::FnDef(_, _) => layout_of_unit(&cx, dl)?, TyKind::FnDef(_, _) => layout_of_unit(&cx)?,
TyKind::Never => cx.layout_of_never_type(), TyKind::Never => cx.calc.layout_of_never_type(),
TyKind::Dyn(_) | TyKind::Foreign(_) => { TyKind::Dyn(_) | TyKind::Foreign(_) => {
let mut unit = layout_of_unit(&cx, dl)?; let mut unit = layout_of_unit(&cx)?;
match &mut unit.abi { match &mut unit.abi {
Abi::Aggregate { sized } => *sized = false, Abi::Aggregate { sized } => *sized = false,
_ => return Err(LayoutError::Unknown), _ => return Err(LayoutError::Unknown),
@ -414,8 +418,7 @@ pub fn layout_of_ty_query(
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
let fields = fields.iter().collect::<IndexVec<_, _>>(); let fields = fields.iter().collect::<IndexVec<_, _>>();
cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized) cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)?
.ok_or(LayoutError::Unknown)?
} }
TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) => { TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) => {
return Err(LayoutError::NotImplemented) return Err(LayoutError::NotImplemented)
@ -447,14 +450,14 @@ pub fn layout_of_ty_recover(
Err(LayoutError::RecursiveTypeWithoutIndirection) Err(LayoutError::RecursiveTypeWithoutIndirection)
} }
fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> { fn layout_of_unit(cx: &LayoutCx<'_>) -> Result<Layout, LayoutError> {
cx.univariant::<RustcFieldIdx, RustcEnumVariantIdx, &&Layout>( cx.calc
dl, .univariant::<RustcFieldIdx, RustcEnumVariantIdx, &&Layout>(
IndexSlice::empty(), IndexSlice::empty(),
&ReprOptions::default(), &ReprOptions::default(),
StructKind::AlwaysSized, StructKind::AlwaysSized,
) )
.ok_or(LayoutError::Unknown) .map_err(Into::into)
} }
fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty {

View file

@ -5,7 +5,7 @@ use std::{cmp, ops::Bound};
use base_db::salsa::Cycle; use base_db::salsa::Cycle;
use hir_def::{ use hir_def::{
data::adt::VariantData, data::adt::VariantData,
layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, layout::{Integer, ReprOptions, TargetDataLayout},
AdtId, VariantId, AdtId, VariantId,
}; };
use intern::sym; use intern::sym;
@ -36,8 +36,8 @@ pub fn layout_of_adt_query(
let Ok(target) = db.target_data_layout(krate) else { let Ok(target) = db.target_data_layout(krate) else {
return Err(LayoutError::TargetLayoutNotAvailable); return Err(LayoutError::TargetLayoutNotAvailable);
}; };
let cx = LayoutCx { target: &target }; let dl = &*target;
let dl = cx.current_data_layout(); let cx = LayoutCx::new(dl);
let handle_variant = |def: VariantId, var: &VariantData| { let handle_variant = |def: VariantId, var: &VariantData| {
var.fields() var.fields()
.iter() .iter()
@ -73,9 +73,9 @@ pub fn layout_of_adt_query(
.collect::<SmallVec<[_; 1]>>(); .collect::<SmallVec<[_; 1]>>();
let variants = variants.iter().map(|it| it.iter().collect()).collect::<IndexVec<_, _>>(); let variants = variants.iter().map(|it| it.iter().collect()).collect::<IndexVec<_, _>>();
let result = if matches!(def, AdtId::UnionId(..)) { let result = if matches!(def, AdtId::UnionId(..)) {
cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)? cx.calc.layout_of_union(&repr, &variants)?
} else { } else {
cx.layout_of_struct_or_enum( cx.calc.layout_of_struct_or_enum(
&repr, &repr,
&variants, &variants,
matches!(def, AdtId::EnumId(..)), matches!(def, AdtId::EnumId(..)),
@ -103,8 +103,7 @@ pub fn layout_of_adt_query(
.next() .next()
.and_then(|it| it.iter().last().map(|it| !it.is_unsized())) .and_then(|it| it.iter().last().map(|it| !it.is_unsized()))
.unwrap_or(true), .unwrap_or(true),
) )?
.ok_or(LayoutError::SizeOverflow)?
}; };
Ok(Arc::new(result)) Ok(Arc::new(result))
} }

View file

@ -1,22 +0,0 @@
//@ known-bug: #124182
struct LazyLock<T> {
data: (Copy, fn() -> T),
}
impl<T> LazyLock<T> {
pub const fn new(f: fn() -> T) -> LazyLock<T> {
LazyLock { data: (None, f) }
}
}
struct A<T = i32>(Option<T>);
impl<T> Default for A<T> {
fn default() -> Self {
A(None)
}
}
static EMPTY_SET: LazyLock<A<i32>> = LazyLock::new(A::default);
fn main() {}

View file

@ -1,21 +1,12 @@
//@ known-bug: rust-lang/rust#126939 //@ known-bug: rust-lang/rust#126939
struct MySlice<T: Copy>(bool, T); struct MySlice<T>(T);
type MySliceBool = MySlice<[bool]>; type MySliceBool = MySlice<[bool]>;
use std::mem; struct P2 {
struct P2<T> {
a: T,
b: MySliceBool, b: MySliceBool,
} }
macro_rules! check { static CHECK: () = assert!(align_of::<P2>() == 1);
($t:ty, $align:expr) => ({
assert_eq!(mem::align_of::<$t>(), $align);
});
}
pub fn main() { fn main() {}
check!(P2<u8>, 1);
}

View file

@ -76,3 +76,8 @@ impl S {
#[rustc_layout(debug)] #[rustc_layout(debug)]
type Impossible = (str, str); //~ ERROR: cannot be known at compilation time type Impossible = (str, str); //~ ERROR: cannot be known at compilation time
// Test that computing the layout of an empty union doesn't ICE.
#[rustc_layout(debug)]
union EmptyUnion {} //~ ERROR: has an unknown layout
//~^ ERROR: unions cannot have zero fields

View file

@ -1,3 +1,9 @@
error: unions cannot have zero fields
--> $DIR/debug.rs:82:1
|
LL | union EmptyUnion {}
| ^^^^^^^^^^^^^^^^^^^
error: layout_of(E) = Layout { error: layout_of(E) = Layout {
size: Size(12 bytes), size: Size(12 bytes),
align: AbiAndPrefAlign { align: AbiAndPrefAlign {
@ -566,12 +572,18 @@ LL | type Impossible = (str, str);
= help: the trait `Sized` is not implemented for `str` = help: the trait `Sized` is not implemented for `str`
= note: only the last element of a tuple may have a dynamically sized type = note: only the last element of a tuple may have a dynamically sized type
error: the type `EmptyUnion` has an unknown layout
--> $DIR/debug.rs:82:1
|
LL | union EmptyUnion {}
| ^^^^^^^^^^^^^^^^
error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases
--> $DIR/debug.rs:74:5 --> $DIR/debug.rs:74:5
| |
LL | const C: () = (); LL | const C: () = ();
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: aborting due to 17 previous errors error: aborting due to 19 previous errors
For more information about this error, try `rustc --explain E0277`. For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,14 @@
// issue: #124182
//! This test used to trip an assertion in const eval, because `layout_of(LazyLock)`
//! returned `Ok` with an unsized layout when a sized layout was expected.
//! It was fixed by making `layout_of` always return `Err` for types that
//! contain unsized fields in unexpected locations.
struct LazyLock {
data: (dyn Sync, ()), //~ ERROR the size for values of type
}
static EMPTY_SET: LazyLock = todo!();
fn main() {}

View file

@ -0,0 +1,12 @@
error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time
--> $DIR/invalid-unsized-const-eval.rs:9:11
|
LL | data: (dyn Sync, ()),
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Sync + 'static)`
= note: only the last element of a tuple may have a dynamically sized type
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,51 @@
//@ check-pass
//! With trivial bounds, it is possible to have ADTs with unsized fields
//! in arbitrary places. Test that we do not ICE for such types.
#![feature(trivial_bounds)]
#![expect(trivial_bounds)]
struct Struct
where
[u8]: Sized,
[i16]: Sized,
{
a: [u8],
b: [i16],
c: f32,
}
union Union
where
[u8]: Copy,
[i16]: Copy,
{
a: [u8],
b: [i16],
c: f32,
}
enum Enum
where
[u8]: Sized,
[i16]: Sized,
{
V1([u8], [i16]),
V2([i16], f32),
}
// This forces layout computation via the `variant_size_differences` lint.
// FIXME: This could be made more robust, possibly with a variant of `rustc_layout`
// that doesn't error.
enum Check
where
[u8]: Copy,
[i16]: Copy,
{
Struct(Struct),
Union(Union),
Enum(Enum),
}
fn main() {}

View file

@ -1,4 +1,9 @@
//@ known-bug: #123134 //@ check-pass
// issue: #123134
//! This is a variant of `trivial-bounds-sized.rs` that compiles without any
//! feature gates and used to trigger a delayed bug.
trait Api: Sized { trait Api: Sized {
type Device: ?Sized; type Device: ?Sized;
} }
@ -7,7 +12,7 @@ struct OpenDevice<A: Api>
where where
A::Device: Sized, A::Device: Sized,
{ {
device: A::Device, device: A::Device, // <- this is the type that ends up being unsized.
queue: (), queue: (),
} }
@ -31,6 +36,8 @@ impl<T> Adapter for T {
fn open() -> OpenDevice<Self::A> fn open() -> OpenDevice<Self::A>
where where
<Self::A as Api>::Device: Sized, <Self::A as Api>::Device: Sized,
// ^ the bound expands to `<<T as Adapter>::A as Api>::Device: Sized`, which
// is not considered trivial due to containing the type parameter `T`
{ {
unreachable!() unreachable!()
} }