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.
This commit is contained in:
parent
3d5528c287
commit
313714331a
3 changed files with 222 additions and 1 deletions
|
@ -8,7 +8,9 @@ use rustc_middle::ty::layout::{
|
||||||
IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
|
IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
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_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
@ -506,6 +508,40 @@ 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 {
|
||||||
|
cx.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 =
|
let get_discriminant_type =
|
||||||
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
|
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
|
||||||
|
|
||||||
|
|
79
tests/ui/layout/ice-non-last-unsized-field-issue-121473.rs
Normal file
79
tests/ui/layout/ice-non-last-unsized-field-issue-121473.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// 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],
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BadEnum1 {
|
||||||
|
Variant1 {
|
||||||
|
field1: i32,
|
||||||
|
field2: str, // Unsized
|
||||||
|
//~^ ERROR the size for values of type `str` cannot be known at compilation time
|
||||||
|
field3: [u8; 16],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BadEnum2 {
|
||||||
|
Variant1(
|
||||||
|
i32,
|
||||||
|
str, // Unsized
|
||||||
|
//~^ ERROR the size for values of type `str` cannot be known at compilation time
|
||||||
|
[u8; 16]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BadEnumMultiVariant {
|
||||||
|
Variant1(i32),
|
||||||
|
Variant2 {
|
||||||
|
field1: i32,
|
||||||
|
field2: str, // Unsized
|
||||||
|
//~^ ERROR the size for values of type `str` cannot be known at compilation time
|
||||||
|
field3: [u8; 16],
|
||||||
|
},
|
||||||
|
Variant3
|
||||||
|
}
|
||||||
|
|
||||||
|
union BadUnion {
|
||||||
|
field1: i32,
|
||||||
|
field2: str, // Unsized
|
||||||
|
//~^ ERROR the size for values of type `str` cannot be known at compilation time
|
||||||
|
//~| ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
|
||||||
|
field3: [u8; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to test that projection type fields that normalize
|
||||||
|
// to a sized type do not cause problems
|
||||||
|
struct StructWithProjections<'a>
|
||||||
|
{
|
||||||
|
field1: <&'a [i32] as IntoIterator>::IntoIter,
|
||||||
|
field2: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let _a = &size_of::<BadStruct>();
|
||||||
|
assert_eq!(size_of::<BadStruct>(), 21);
|
||||||
|
|
||||||
|
let _a = &size_of::<BadEnum1>();
|
||||||
|
assert_eq!(size_of::<BadEnum1>(), 21);
|
||||||
|
|
||||||
|
let _a = &size_of::<BadEnum2>();
|
||||||
|
assert_eq!(size_of::<BadEnum2>(), 21);
|
||||||
|
|
||||||
|
let _a = &size_of::<BadEnumMultiVariant>();
|
||||||
|
assert_eq!(size_of::<BadEnumMultiVariant>(), 21);
|
||||||
|
|
||||||
|
let _a = &size_of::<BadUnion>();
|
||||||
|
assert_eq!(size_of::<BadUnion>(), 21);
|
||||||
|
|
||||||
|
let _a = &size_of::<StructWithProjections>();
|
||||||
|
assert_eq!(size_of::<StructWithProjections>(), 21);
|
||||||
|
let _a = StructWithProjections { field1: [1, 3].iter(), field2: 3 };
|
||||||
|
}
|
106
tests/ui/layout/ice-non-last-unsized-field-issue-121473.stderr
Normal file
106
tests/ui/layout/ice-non-last-unsized-field-issue-121473.stderr
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
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[E0277]: the size for values of type `str` cannot be known at compilation time
|
||||||
|
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:18:17
|
||||||
|
|
|
||||||
|
LL | field2: str, // Unsized
|
||||||
|
| ^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: the trait `Sized` is not implemented for `str`
|
||||||
|
= note: no field of an enum variant 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 | field2: &str, // Unsized
|
||||||
|
| +
|
||||||
|
help: the `Box` type always has a statically known size and allocates its contents in the heap
|
||||||
|
|
|
||||||
|
LL | field2: Box<str>, // Unsized
|
||||||
|
| ++++ +
|
||||||
|
|
||||||
|
error[E0277]: the size for values of type `str` cannot be known at compilation time
|
||||||
|
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:27:9
|
||||||
|
|
|
||||||
|
LL | str, // Unsized
|
||||||
|
| ^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: the trait `Sized` is not implemented for `str`
|
||||||
|
= note: no field of an enum variant 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 | &str, // Unsized
|
||||||
|
| +
|
||||||
|
help: the `Box` type always has a statically known size and allocates its contents in the heap
|
||||||
|
|
|
||||||
|
LL | Box<str>, // Unsized
|
||||||
|
| ++++ +
|
||||||
|
|
||||||
|
error[E0277]: the size for values of type `str` cannot be known at compilation time
|
||||||
|
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:37:17
|
||||||
|
|
|
||||||
|
LL | field2: str, // Unsized
|
||||||
|
| ^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: the trait `Sized` is not implemented for `str`
|
||||||
|
= note: no field of an enum variant 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 | field2: &str, // Unsized
|
||||||
|
| +
|
||||||
|
help: the `Box` type always has a statically known size and allocates its contents in the heap
|
||||||
|
|
|
||||||
|
LL | field2: Box<str>, // Unsized
|
||||||
|
| ++++ +
|
||||||
|
|
||||||
|
error[E0277]: the size for values of type `str` cannot be known at compilation time
|
||||||
|
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:46:13
|
||||||
|
|
|
||||||
|
LL | field2: str, // Unsized
|
||||||
|
| ^^^ doesn't have a size known at compile-time
|
||||||
|
|
|
||||||
|
= help: the trait `Sized` is not implemented for `str`
|
||||||
|
= note: no field of a union 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 | field2: &str, // Unsized
|
||||||
|
| +
|
||||||
|
help: the `Box` type always has a statically known size and allocates its contents in the heap
|
||||||
|
|
|
||||||
|
LL | field2: Box<str>, // Unsized
|
||||||
|
| ++++ +
|
||||||
|
|
||||||
|
error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
|
||||||
|
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:46:5
|
||||||
|
|
|
||||||
|
LL | field2: str, // Unsized
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
|
||||||
|
help: wrap the field type in `ManuallyDrop<...>`
|
||||||
|
|
|
||||||
|
LL | field2: std::mem::ManuallyDrop<str>, // Unsized
|
||||||
|
| +++++++++++++++++++++++ +
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0277, E0740.
|
||||||
|
For more information about an error, try `rustc --explain E0277`.
|
Loading…
Add table
Add a link
Reference in a new issue