Skip to content

Commit

Permalink
Error out of layout calculation if a non-last struct field is unsized
Browse files Browse the repository at this point in the history
Fixes an ICE that occurs when a struct with an unsized field
at a non-last position is const evaluated.
  • Loading branch information
gurry committed Feb 24, 2024
1 parent 8f359be commit 586d57e
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
28 changes: 22 additions & 6 deletions compiler/rustc_ty_utils/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use rustc_middle::ty::layout::{
IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{
self, AdtDef, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt,
};
use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
use rustc_span::symbol::Symbol;
use rustc_target::abi::*;
Expand Down Expand Up @@ -503,6 +505,23 @@ fn layout_of_uncached<'tcx>(
));
}

let is_unsized_field = |field: &FieldDef| {
let param_env = tcx.param_env(def.did());
!tcx.type_of(field.did).instantiate_identity().is_sized(tcx, param_env)
};

if def.is_struct()
&& let Some((_, fields_except_last)) =
def.non_enum_variant().fields.raw.split_last()
&& fields_except_last.iter().any(is_unsized_field)
{
cx.tcx.dcx().span_delayed_bug(
tcx.def_span(def.did()),
"only the last field of a struct can be unsized",
);
return Err(error(cx, LayoutError::Unknown(ty)));
}

let get_discriminant_type =
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);

Expand All @@ -519,11 +538,8 @@ fn layout_of_uncached<'tcx>(
.iter_enumerated()
.any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()));

let maybe_unsized = def.is_struct()
&& def.non_enum_variant().tail_opt().is_some_and(|last_field| {
let param_env = tcx.param_env(def.did());
!tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, param_env)
});
let maybe_unsized =
def.is_struct() && def.non_enum_variant().tail_opt().is_some_and(is_unsized_field);

let Some(layout) = cx.layout_of_struct_or_enum(
&def.repr(),
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/layout/ice-non-last-unsized-field-issue-121473.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Regression test for #121473
// Checks that no ICE occurs when `size_of`
// is applied to a struct that has an unsized
// field which is not its last field

use std::mem::size_of;

pub struct BadStruct {
pub field1: i32,
pub field2: str, // Unsized field that is not the last field
//~^ ERROR the size for values of type `str` cannot be known at compilation time
pub field3: [u8; 16],
}

pub fn main() {
// The ICE occurs only in promoted MIR
let _x = &size_of::<BadStruct>();
assert_eq!(size_of::<BadStruct>(), 21);
}
21 changes: 21 additions & 0 deletions tests/ui/layout/ice-non-last-unsized-field-issue-121473.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:10:17
|
LL | pub field2: str, // Unsized field that is not the last field
| ^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
= note: only the last field of a struct may have a dynamically sized type
= help: change the field's type to have a statically known size
help: borrowed types always have a statically known size
|
LL | pub field2: &str, // Unsized field that is not the last field
| +
help: the `Box` type always has a statically known size and allocates its contents in the heap
|
LL | pub field2: Box<str>, // Unsized field that is not the last field
| ++++ +

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 586d57e

Please sign in to comment.