Skip to content

Commit a658791

Browse files
committed
Error out of layout calculation if a non-last struct field is unsized
Fixes an ICE that occurs when a struct with an unsized field at a non-last position is const evaluated.
1 parent 3d5528c commit a658791

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

Diff for: compiler/rustc_ty_utils/src/layout.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use rustc_middle::ty::layout::{
88
IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
99
};
1010
use rustc_middle::ty::print::with_no_trimmed_paths;
11-
use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
11+
use rustc_middle::ty::{
12+
self, AdtDef, EarlyBinder, FieldDef, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt,
13+
};
1214
use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
1315
use rustc_span::sym;
1416
use rustc_span::symbol::Symbol;
@@ -506,6 +508,33 @@ fn layout_of_uncached<'tcx>(
506508
));
507509
}
508510

511+
let is_unsized_field = |field: &FieldDef| {
512+
let field_ty = tcx.type_of(field.did);
513+
tcx.try_instantiate_and_normalize_erasing_regions(args, cx.param_env, field_ty)
514+
.map(|f| !f.is_sized(tcx, cx.param_env))
515+
.map_err(|e| {
516+
error(
517+
cx,
518+
LayoutError::NormalizationFailure(field_ty.instantiate_identity(), e),
519+
)
520+
})
521+
};
522+
523+
if def.is_struct()
524+
&& let Some((_, fields_except_last)) =
525+
def.non_enum_variant().fields.raw.split_last()
526+
{
527+
for f in fields_except_last {
528+
if is_unsized_field(f)? {
529+
cx.tcx.dcx().span_delayed_bug(
530+
tcx.def_span(def.did()),
531+
"only the last field of a struct can be unsized",
532+
);
533+
return Err(error(cx, LayoutError::Unknown(ty)));
534+
}
535+
}
536+
}
537+
509538
let get_discriminant_type =
510539
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
511540

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Regression test for #121473
2+
// Checks that no ICE occurs when `size_of`
3+
// is applied to a struct that has an unsized
4+
// field which is not its last field
5+
6+
use std::mem::size_of;
7+
8+
pub struct BadStruct {
9+
pub field1: i32,
10+
pub field2: str, // Unsized field that is not the last field
11+
//~^ ERROR the size for values of type `str` cannot be known at compilation time
12+
pub field3: [u8; 16],
13+
}
14+
15+
// Tests that projection type fields that normalize
16+
// to a sized type should not cause problems
17+
struct StructWithProjections<'a>
18+
{
19+
field1: <&'a [i32] as IntoIterator>::IntoIter,
20+
field2: i32
21+
}
22+
23+
pub fn main() {
24+
let _a = &size_of::<BadStruct>();
25+
assert_eq!(size_of::<BadStruct>(), 21);
26+
27+
let _a = &size_of::<StructWithProjections>();
28+
let _a = StructWithProjections { field1: [1, 3].iter(), field2: 3 };
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0277]: the size for values of type `str` cannot be known at compilation time
2+
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:10:17
3+
|
4+
LL | pub field2: str, // Unsized field that is not the last field
5+
| ^^^ doesn't have a size known at compile-time
6+
|
7+
= help: the trait `Sized` is not implemented for `str`
8+
= note: only the last field of a struct may have a dynamically sized type
9+
= help: change the field's type to have a statically known size
10+
help: borrowed types always have a statically known size
11+
|
12+
LL | pub field2: &str, // Unsized field that is not the last field
13+
| +
14+
help: the `Box` type always has a statically known size and allocates its contents in the heap
15+
|
16+
LL | pub field2: Box<str>, // Unsized field that is not the last field
17+
| ++++ +
18+
19+
error: aborting due to 1 previous error
20+
21+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)