Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 586d57e

Browse files
committedFeb 24, 2024
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 8f359be commit 586d57e

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed
 

‎compiler/rustc_ty_utils/src/layout.rs

+22-6
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::symbol::Symbol;
1416
use rustc_target::abi::*;
@@ -503,6 +505,23 @@ fn layout_of_uncached<'tcx>(
503505
));
504506
}
505507

508+
let is_unsized_field = |field: &FieldDef| {
509+
let param_env = tcx.param_env(def.did());
510+
!tcx.type_of(field.did).instantiate_identity().is_sized(tcx, param_env)
511+
};
512+
513+
if def.is_struct()
514+
&& let Some((_, fields_except_last)) =
515+
def.non_enum_variant().fields.raw.split_last()
516+
&& fields_except_last.iter().any(is_unsized_field)
517+
{
518+
cx.tcx.dcx().span_delayed_bug(
519+
tcx.def_span(def.did()),
520+
"only the last field of a struct can be unsized",
521+
);
522+
return Err(error(cx, LayoutError::Unknown(ty)));
523+
}
524+
506525
let get_discriminant_type =
507526
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
508527

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

522-
let maybe_unsized = def.is_struct()
523-
&& def.non_enum_variant().tail_opt().is_some_and(|last_field| {
524-
let param_env = tcx.param_env(def.did());
525-
!tcx.type_of(last_field.did).instantiate_identity().is_sized(tcx, param_env)
526-
});
541+
let maybe_unsized =
542+
def.is_struct() && def.non_enum_variant().tail_opt().is_some_and(is_unsized_field);
527543

528544
let Some(layout) = cx.layout_of_struct_or_enum(
529545
&def.repr(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
pub fn main() {
16+
// The ICE occurs only in promoted MIR
17+
let _x = &size_of::<BadStruct>();
18+
assert_eq!(size_of::<BadStruct>(), 21);
19+
}
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)
Please sign in to comment.