diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index e825754449892..f4de4e06d1b07 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1148,7 +1148,11 @@ impl LayoutCalculator { } if field.is_unsized() { - unsized_field = Some(field); + if let StructKind::MaybeUnsized = kind { + unsized_field = Some(field); + } else { + return Err(LayoutCalculatorError::UnexpectedUnsized(*field)); + } } // Invariant: offset < dl.obj_size_bound() <= 1<<61 diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 1d4c732242b0e..c153fb64c333b 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -13,8 +13,7 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, AdtDef, CoroutineArgsExt, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, - TypeVisitableExt, + self, AdtDef, CoroutineArgsExt, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, }; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use rustc_span::sym; @@ -572,40 +571,6 @@ fn layout_of_uncached<'tcx>( )); } - let err_if_unsized = |field: &FieldDef, err_msg: &str| { - let field_ty = tcx.type_of(field.did); - let is_unsized = tcx - .try_instantiate_and_normalize_erasing_regions(args, cx.param_env, field_ty) - .map(|f| !f.is_sized(tcx, cx.param_env)) - .map_err(|e| { - error( - cx, - LayoutError::NormalizationFailure(field_ty.instantiate_identity(), e), - ) - })?; - - if is_unsized { - tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned()); - Err(error(cx, LayoutError::Unknown(ty))) - } else { - Ok(()) - } - }; - - if def.is_struct() { - if let Some((_, fields_except_last)) = - def.non_enum_variant().fields.raw.split_last() - { - for f in fields_except_last { - err_if_unsized(f, "only the last field of a struct can be unsized")?; - } - } - } else { - for f in def.all_fields() { - err_if_unsized(f, &format!("{}s cannot have unsized fields", def.descr()))?; - } - } - let get_discriminant_type = |min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max); @@ -643,6 +608,10 @@ fn layout_of_uncached<'tcx>( ) .map_err(|err| map_error(cx, ty, err))?; + if !maybe_unsized && layout.is_unsized() { + bug!("got unsized layout for type that cannot be unsized {ty:?}: {layout:#?}"); + } + // If the struct tail is sized and can be unsized, check that unsizing doesn't move the fields around. if cfg!(debug_assertions) && maybe_unsized diff --git a/tests/crashes/126939.rs b/tests/crashes/126939.rs deleted file mode 100644 index 07bafd35420ee..0000000000000 --- a/tests/crashes/126939.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: rust-lang/rust#126939 - -struct MySlice(T); -type MySliceBool = MySlice<[bool]>; - -struct P2 { - b: MySliceBool, -} - -static CHECK: () = assert!(align_of::() == 1); - -fn main() {} diff --git a/tests/crashes/127737.rs b/tests/ui/layout/invalid-unsized-const-prop.rs similarity index 65% rename from tests/crashes/127737.rs rename to tests/ui/layout/invalid-unsized-const-prop.rs index 2ee8c769858a8..2f1c398c35a41 100644 --- a/tests/crashes/127737.rs +++ b/tests/ui/layout/invalid-unsized-const-prop.rs @@ -1,6 +1,10 @@ -//@ known-bug: #127737 +// issue: #127737 +//@ check-pass //@ compile-flags: -Zmir-opt-level=5 --crate-type lib +//! This test is very similar to `invalid-unsized-const-eval.rs`, but also requires +//! checking for unsized types in the last field of each enum variant. + pub trait TestTrait { type MyType; fn func() -> Option diff --git a/tests/ui/layout/invalid-unsized-in-always-sized-tail.rs b/tests/ui/layout/invalid-unsized-in-always-sized-tail.rs new file mode 100644 index 0000000000000..5df97338710dd --- /dev/null +++ b/tests/ui/layout/invalid-unsized-in-always-sized-tail.rs @@ -0,0 +1,17 @@ +// issue: rust-lang/rust#126939 + +//! This used to ICE, because the layout algorithm did not check for unsized types +//! in the struct tail of always-sized types (i.e. those that cannot be unsized) +//! and incorrectly returned an unsized layout. + +struct MySlice(T); +type MySliceBool = MySlice<[bool]>; + +struct P2 { + b: MySliceBool, + //~^ ERROR: the size for values of type `[bool]` cannot be known at compilation time +} + +static CHECK: () = assert!(align_of::() == 1); + +fn main() {} diff --git a/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr b/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr new file mode 100644 index 0000000000000..3f565d6ee5ba8 --- /dev/null +++ b/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr @@ -0,0 +1,23 @@ +error[E0277]: the size for values of type `[bool]` cannot be known at compilation time + --> $DIR/invalid-unsized-in-always-sized-tail.rs:11:8 + | +LL | b: MySliceBool, + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[bool]` +note: required by an implicit `Sized` bound in `MySlice` + --> $DIR/invalid-unsized-in-always-sized-tail.rs:7:16 + | +LL | struct MySlice(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `MySlice` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/invalid-unsized-in-always-sized-tail.rs:7:16 + | +LL | struct MySlice(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.