assert that unexpectedly unsized fields are sized in the param env

This commit is contained in:
Lukas Markeffsky 2024-09-16 21:04:20 +02:00
parent 697450151c
commit 3db930a463
4 changed files with 64 additions and 57 deletions

View File

@ -36,12 +36,14 @@ enum NicheBias {
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum LayoutCalculatorError { pub enum LayoutCalculatorError<F> {
/// An unsized type was found in a location where a sized type was expected. /// 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` /// This is not always a compile error, for example if there is a `[T]: Sized`
/// bound in a where clause. /// bound in a where clause.
UnexpectedUnsized, ///
/// Contains the field that was unexpectedly unsized.
UnexpectedUnsized(F),
/// A type was too large for the target platform. /// A type was too large for the target platform.
SizeOverflow, SizeOverflow,
@ -50,8 +52,8 @@ pub enum LayoutCalculatorError {
EmptyUnion, EmptyUnion,
} }
type LayoutCalculatorResult<FieldIdx, VariantIdx> = type LayoutCalculatorResult<FieldIdx, VariantIdx, F> =
Result<LayoutS<FieldIdx, VariantIdx>, LayoutCalculatorError>; Result<LayoutS<FieldIdx, VariantIdx>, LayoutCalculatorError<F>>;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct LayoutCalculator<Cx> { pub struct LayoutCalculator<Cx> {
@ -100,13 +102,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
'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 + Copy,
>( >(
&self, &self,
fields: &IndexSlice<FieldIdx, F>, fields: &IndexSlice<FieldIdx, F>,
repr: &ReprOptions, repr: &ReprOptions,
kind: StructKind, kind: StructKind,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let dl = self.cx.data_layout(); let dl = self.cx.data_layout();
let layout = self.univariant_biased(fields, repr, kind, NicheBias::Start); 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
@ -191,7 +193,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
'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 + Copy,
>( >(
&self, &self,
repr: &ReprOptions, repr: &ReprOptions,
@ -203,7 +205,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
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,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let (present_first, present_second) = { let (present_first, present_second) = {
let mut present_variants = variants let mut present_variants = variants
.iter_enumerated() .iter_enumerated()
@ -254,12 +256,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
'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 + Copy,
>( >(
&self, &self,
repr: &ReprOptions, repr: &ReprOptions,
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>, variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let dl = self.cx.data_layout(); let dl = self.cx.data_layout();
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;
@ -279,7 +281,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
let only_variant = &variants[only_variant_idx]; 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() {
return Err(LayoutCalculatorError::UnexpectedUnsized); return Err(LayoutCalculatorError::UnexpectedUnsized(*field));
} }
align = align.max(field.align); align = align.max(field.align);
@ -359,7 +361,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
} }
/// 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, FieldIdx: Idx, VariantIdx: Idx, F>( fn layout_of_struct<
'a,
FieldIdx: Idx,
VariantIdx: Idx,
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
>(
&self, &self,
repr: &ReprOptions, repr: &ReprOptions,
variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>, variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
@ -368,10 +375,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
scalar_valid_range: (Bound<u128>, Bound<u128>), scalar_valid_range: (Bound<u128>, Bound<u128>),
always_sized: bool, always_sized: bool,
present_first: VariantIdx, present_first: VariantIdx,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx> ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
where
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.)
@ -457,17 +461,19 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
Ok(st) Ok(st)
} }
fn layout_of_enum<'a, FieldIdx: Idx, VariantIdx: Idx, F>( fn layout_of_enum<
'a,
FieldIdx: Idx,
VariantIdx: Idx,
F: Deref<Target = &'a LayoutS<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
>(
&self, &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,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx> ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
where
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
@ -972,14 +978,14 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
'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 + Copy,
>( >(
&self, &self,
fields: &IndexSlice<FieldIdx, F>, fields: &IndexSlice<FieldIdx, F>,
repr: &ReprOptions, repr: &ReprOptions,
kind: StructKind, kind: StructKind,
niche_bias: NicheBias, niche_bias: NicheBias,
) -> LayoutCalculatorResult<FieldIdx, VariantIdx> { ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
let dl = self.cx.data_layout(); 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 };
@ -1124,7 +1130,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
// field 5 with offset 0 puts 0 in offsets[5]. // field 5 with offset 0 puts 0 in offsets[5].
// At the bottom of this function, we invert `inverse_memory_index` to // At the bottom of this function, we invert `inverse_memory_index` to
// produce `memory_index` (see `invert_mapping`). // produce `memory_index` (see `invert_mapping`).
let mut sized = true; let mut unsized_field = None::<&F>;
let mut offsets = IndexVec::from_elem(Size::ZERO, fields); let mut offsets = IndexVec::from_elem(Size::ZERO, fields);
let mut offset = Size::ZERO; let mut offset = Size::ZERO;
let mut largest_niche = None; let mut largest_niche = None;
@ -1137,12 +1143,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
} }
for &i in &inverse_memory_index { for &i in &inverse_memory_index {
let field = &fields[i]; let field = &fields[i];
if !sized { if let Some(unsized_field) = unsized_field {
return Err(LayoutCalculatorError::UnexpectedUnsized); return Err(LayoutCalculatorError::UnexpectedUnsized(*unsized_field));
} }
if field.is_unsized() { if field.is_unsized() {
sized = false; unsized_field = Some(field);
} }
// Invariant: offset < dl.obj_size_bound() <= 1<<61 // Invariant: offset < dl.obj_size_bound() <= 1<<61
@ -1206,6 +1212,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
return Err(LayoutCalculatorError::SizeOverflow); return Err(LayoutCalculatorError::SizeOverflow);
} }
let mut layout_of_single_non_zst_field = None; let mut layout_of_single_non_zst_field = None;
let sized = unsized_field.is_none();
let mut abi = Abi::Aggregate { sized }; let mut abi = Abi::Aggregate { sized };
let optimize_abi = !repr.inhibit_newtype_abi_optimization(); let optimize_abi = !repr.inhibit_newtype_abi_optimization();

View File

@ -20,7 +20,7 @@ pub struct IndexSlice<I: Idx, T> {
impl<I: Idx, T> IndexSlice<I, T> { impl<I: Idx, T> IndexSlice<I, T> {
#[inline] #[inline]
pub const fn empty() -> &'static Self { pub const fn empty<'a>() -> &'a Self {
Self::from_raw(&[]) Self::from_raw(&[])
} }

View File

@ -86,19 +86,21 @@ fn error<'tcx>(cx: &LayoutCx<'tcx>, err: LayoutError<'tcx>) -> &'tcx LayoutError
fn map_error<'tcx>( fn map_error<'tcx>(
cx: &LayoutCx<'tcx>, cx: &LayoutCx<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
err: LayoutCalculatorError, err: LayoutCalculatorError<TyAndLayout<'tcx>>,
) -> &'tcx LayoutError<'tcx> { ) -> &'tcx LayoutError<'tcx> {
let err = match err { let err = match err {
LayoutCalculatorError::SizeOverflow => { LayoutCalculatorError::SizeOverflow => {
// This is sometimes not a compile error in `check` builds. // This is sometimes not a compile error in `check` builds.
// See `tests/ui/limits/huge-enum.rs` for an example.
LayoutError::SizeOverflow(ty) LayoutError::SizeOverflow(ty)
} }
LayoutCalculatorError::UnexpectedUnsized => { LayoutCalculatorError::UnexpectedUnsized(field) => {
// This is sometimes not a compile error if there are trivially false where // This is sometimes not a compile error if there are trivially false where clauses.
// clauses, but it is always a compiler error in the empty environment. // See `tests/ui/layout/trivial-bounds-sized.rs` for an example.
if cx.param_env.caller_bounds().is_empty() { assert!(field.layout.is_unsized(), "invalid layout error {err:#?}");
if !field.ty.is_sized(cx.tcx(), cx.param_env) {
cx.tcx().dcx().delayed_bug(format!( cx.tcx().dcx().delayed_bug(format!(
"encountered unexpected unsized field in layout of {ty:?}" "encountered unexpected unsized field in layout of {ty:?}: {field:#?}"
)); ));
} }
LayoutError::Unknown(ty) LayoutError::Unknown(ty)
@ -115,7 +117,7 @@ fn map_error<'tcx>(
fn univariant_uninterned<'tcx>( fn univariant_uninterned<'tcx>(
cx: &LayoutCx<'tcx>, cx: &LayoutCx<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
fields: &IndexSlice<FieldIdx, Layout<'_>>, fields: &IndexSlice<FieldIdx, TyAndLayout<'tcx>>,
repr: &ReprOptions, repr: &ReprOptions,
kind: StructKind, kind: StructKind,
) -> Result<LayoutS<FieldIdx, VariantIdx>, &'tcx LayoutError<'tcx>> { ) -> Result<LayoutS<FieldIdx, VariantIdx>, &'tcx LayoutError<'tcx>> {
@ -148,9 +150,10 @@ fn layout_of_uncached<'tcx>(
}; };
let scalar = |value: Primitive| tcx.mk_layout(LayoutS::scalar(cx, scalar_unit(value))); let scalar = |value: Primitive| tcx.mk_layout(LayoutS::scalar(cx, scalar_unit(value)));
let univariant = |fields: &IndexSlice<FieldIdx, Layout<'_>>, repr: &ReprOptions, kind| { let univariant =
Ok(tcx.mk_layout(univariant_uninterned(cx, ty, fields, repr, kind)?)) |fields: &IndexSlice<FieldIdx, TyAndLayout<'tcx>>, repr: &ReprOptions, kind| {
}; Ok(tcx.mk_layout(univariant_uninterned(cx, ty, fields, repr, kind)?))
};
debug_assert!(!ty.has_non_region_infer()); debug_assert!(!ty.has_non_region_infer());
Ok(match *ty.kind() { Ok(match *ty.kind() {
@ -388,9 +391,7 @@ fn layout_of_uncached<'tcx>(
ty::Closure(_, args) => { ty::Closure(_, args) => {
let tys = args.as_closure().upvar_tys(); let tys = args.as_closure().upvar_tys();
univariant( univariant(
&tys.iter() &tys.iter().map(|ty| cx.layout_of(ty)).try_collect::<IndexVec<_, _>>()?,
.map(|ty| Ok(cx.layout_of(ty)?.layout))
.try_collect::<IndexVec<_, _>>()?,
&ReprOptions::default(), &ReprOptions::default(),
StructKind::AlwaysSized, StructKind::AlwaysSized,
)? )?
@ -399,9 +400,7 @@ fn layout_of_uncached<'tcx>(
ty::CoroutineClosure(_, args) => { ty::CoroutineClosure(_, args) => {
let tys = args.as_coroutine_closure().upvar_tys(); let tys = args.as_coroutine_closure().upvar_tys();
univariant( univariant(
&tys.iter() &tys.iter().map(|ty| cx.layout_of(ty)).try_collect::<IndexVec<_, _>>()?,
.map(|ty| Ok(cx.layout_of(ty)?.layout))
.try_collect::<IndexVec<_, _>>()?,
&ReprOptions::default(), &ReprOptions::default(),
StructKind::AlwaysSized, StructKind::AlwaysSized,
)? )?
@ -412,7 +411,7 @@ fn layout_of_uncached<'tcx>(
if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
univariant( univariant(
&tys.iter().map(|k| Ok(cx.layout_of(k)?.layout)).try_collect::<IndexVec<_, _>>()?, &tys.iter().map(|k| cx.layout_of(k)).try_collect::<IndexVec<_, _>>()?,
&ReprOptions::default(), &ReprOptions::default(),
kind, kind,
)? )?
@ -552,7 +551,7 @@ fn layout_of_uncached<'tcx>(
.map(|v| { .map(|v| {
v.fields v.fields
.iter() .iter()
.map(|field| Ok(cx.layout_of(field.ty(tcx, args))?.layout)) .map(|field| cx.layout_of(field.ty(tcx, args)))
.try_collect::<IndexVec<_, _>>() .try_collect::<IndexVec<_, _>>()
}) })
.try_collect::<IndexVec<VariantIdx, _>>()?; .try_collect::<IndexVec<VariantIdx, _>>()?;
@ -651,7 +650,7 @@ fn layout_of_uncached<'tcx>(
{ {
let mut variants = variants; let mut variants = variants;
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;
let Ok(unsized_layout) = cx.calc.layout_of_struct_or_enum( let Ok(unsized_layout) = cx.calc.layout_of_struct_or_enum(
&def.repr(), &def.repr(),
@ -859,21 +858,24 @@ fn coroutine_layout<'tcx>(
let max_discr = (info.variant_fields.len() - 1) as u128; let max_discr = (info.variant_fields.len() - 1) as u128;
let discr_int = Integer::fit_unsigned(max_discr); let discr_int = Integer::fit_unsigned(max_discr);
let tag = Scalar::Initialized { let tag = Scalar::Initialized {
value: Primitive::Int(discr_int, false), value: Primitive::Int(discr_int, /* signed = */ false),
valid_range: WrappingRange { start: 0, end: max_discr }, valid_range: WrappingRange { start: 0, end: max_discr },
}; };
let tag_layout = tcx.mk_layout(LayoutS::scalar(cx, tag)); let tag_layout = TyAndLayout {
ty: discr_int.to_ty(tcx, /* signed = */ false),
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);
let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty); let uninit_ty = Ty::new_maybe_uninit(tcx, field_ty);
Ok(cx.spanned_layout_of(uninit_ty, info.field_tys[local].source_info.span)?.layout) cx.spanned_layout_of(uninit_ty, info.field_tys[local].source_info.span)
}); });
let prefix_layouts = args let prefix_layouts = args
.as_coroutine() .as_coroutine()
.prefix_tys() .prefix_tys()
.iter() .iter()
.map(|ty| Ok(cx.layout_of(ty)?.layout)) .map(|ty| cx.layout_of(ty))
.chain(iter::once(Ok(tag_layout))) .chain(iter::once(Ok(tag_layout)))
.chain(promoted_layouts) .chain(promoted_layouts)
.try_collect::<IndexVec<_, _>>()?; .try_collect::<IndexVec<_, _>>()?;
@ -947,9 +949,7 @@ fn coroutine_layout<'tcx>(
let mut variant = univariant_uninterned( let mut variant = univariant_uninterned(
cx, cx,
ty, ty,
&variant_only_tys &variant_only_tys.map(|ty| cx.layout_of(ty)).try_collect::<IndexVec<_, _>>()?,
.map(|ty| Ok(cx.layout_of(ty)?.layout))
.try_collect::<IndexVec<_, _>>()?,
&ReprOptions::default(), &ReprOptions::default(),
StructKind::Prefixed(prefix_size, prefix_align.abi), StructKind::Prefixed(prefix_size, prefix_align.abi),
)?; )?;

View File

@ -106,10 +106,10 @@ impl fmt::Display for LayoutError {
} }
} }
impl From<LayoutCalculatorError> for LayoutError { impl<F> From<LayoutCalculatorError<F>> for LayoutError {
fn from(err: LayoutCalculatorError) -> Self { fn from(err: LayoutCalculatorError<F>) -> Self {
match err { match err {
LayoutCalculatorError::UnexpectedUnsized | LayoutCalculatorError::EmptyUnion => { LayoutCalculatorError::UnexpectedUnsized(_) | LayoutCalculatorError::EmptyUnion => {
LayoutError::Unknown LayoutError::Unknown
} }
LayoutCalculatorError::SizeOverflow => LayoutError::SizeOverflow, LayoutCalculatorError::SizeOverflow => LayoutError::SizeOverflow,