Skip to content

Commit 3137143

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 3137143

File tree

3 files changed

+222
-1
lines changed

3 files changed

+222
-1
lines changed

compiler/rustc_ty_utils/src/layout.rs

+37-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,40 @@ fn layout_of_uncached<'tcx>(
506508
));
507509
}
508510

511+
let err_if_unsized = |field: &FieldDef, err_msg: &str| {
512+
let field_ty = tcx.type_of(field.did);
513+
let is_unsized = tcx
514+
.try_instantiate_and_normalize_erasing_regions(args, cx.param_env, field_ty)
515+
.map(|f| !f.is_sized(tcx, cx.param_env))
516+
.map_err(|e| {
517+
error(
518+
cx,
519+
LayoutError::NormalizationFailure(field_ty.instantiate_identity(), e),
520+
)
521+
})?;
522+
523+
if is_unsized {
524+
cx.tcx.dcx().span_delayed_bug(tcx.def_span(def.did()), err_msg.to_owned());
525+
Err(error(cx, LayoutError::Unknown(ty)))
526+
} else {
527+
Ok(())
528+
}
529+
};
530+
531+
if def.is_struct() {
532+
if let Some((_, fields_except_last)) =
533+
def.non_enum_variant().fields.raw.split_last()
534+
{
535+
for f in fields_except_last {
536+
err_if_unsized(f, "only the last field of a struct can be unsized")?;
537+
}
538+
}
539+
} else {
540+
for f in def.all_fields() {
541+
err_if_unsized(f, &format!("{}s cannot have unsized fields", def.descr()))?;
542+
}
543+
}
544+
509545
let get_discriminant_type =
510546
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
511547

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
enum BadEnum1 {
16+
Variant1 {
17+
field1: i32,
18+
field2: str, // Unsized
19+
//~^ ERROR the size for values of type `str` cannot be known at compilation time
20+
field3: [u8; 16],
21+
},
22+
}
23+
24+
enum BadEnum2 {
25+
Variant1(
26+
i32,
27+
str, // Unsized
28+
//~^ ERROR the size for values of type `str` cannot be known at compilation time
29+
[u8; 16]
30+
),
31+
}
32+
33+
enum BadEnumMultiVariant {
34+
Variant1(i32),
35+
Variant2 {
36+
field1: i32,
37+
field2: str, // Unsized
38+
//~^ ERROR the size for values of type `str` cannot be known at compilation time
39+
field3: [u8; 16],
40+
},
41+
Variant3
42+
}
43+
44+
union BadUnion {
45+
field1: i32,
46+
field2: str, // Unsized
47+
//~^ ERROR the size for values of type `str` cannot be known at compilation time
48+
//~| ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
49+
field3: [u8; 16],
50+
}
51+
52+
// Used to test that projection type fields that normalize
53+
// to a sized type do not cause problems
54+
struct StructWithProjections<'a>
55+
{
56+
field1: <&'a [i32] as IntoIterator>::IntoIter,
57+
field2: i32
58+
}
59+
60+
pub fn main() {
61+
let _a = &size_of::<BadStruct>();
62+
assert_eq!(size_of::<BadStruct>(), 21);
63+
64+
let _a = &size_of::<BadEnum1>();
65+
assert_eq!(size_of::<BadEnum1>(), 21);
66+
67+
let _a = &size_of::<BadEnum2>();
68+
assert_eq!(size_of::<BadEnum2>(), 21);
69+
70+
let _a = &size_of::<BadEnumMultiVariant>();
71+
assert_eq!(size_of::<BadEnumMultiVariant>(), 21);
72+
73+
let _a = &size_of::<BadUnion>();
74+
assert_eq!(size_of::<BadUnion>(), 21);
75+
76+
let _a = &size_of::<StructWithProjections>();
77+
assert_eq!(size_of::<StructWithProjections>(), 21);
78+
let _a = StructWithProjections { field1: [1, 3].iter(), field2: 3 };
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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[E0277]: the size for values of type `str` cannot be known at compilation time
20+
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:18:17
21+
|
22+
LL | field2: str, // Unsized
23+
| ^^^ doesn't have a size known at compile-time
24+
|
25+
= help: the trait `Sized` is not implemented for `str`
26+
= note: no field of an enum variant may have a dynamically sized type
27+
= help: change the field's type to have a statically known size
28+
help: borrowed types always have a statically known size
29+
|
30+
LL | field2: &str, // Unsized
31+
| +
32+
help: the `Box` type always has a statically known size and allocates its contents in the heap
33+
|
34+
LL | field2: Box<str>, // Unsized
35+
| ++++ +
36+
37+
error[E0277]: the size for values of type `str` cannot be known at compilation time
38+
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:27:9
39+
|
40+
LL | str, // Unsized
41+
| ^^^ doesn't have a size known at compile-time
42+
|
43+
= help: the trait `Sized` is not implemented for `str`
44+
= note: no field of an enum variant may have a dynamically sized type
45+
= help: change the field's type to have a statically known size
46+
help: borrowed types always have a statically known size
47+
|
48+
LL | &str, // Unsized
49+
| +
50+
help: the `Box` type always has a statically known size and allocates its contents in the heap
51+
|
52+
LL | Box<str>, // Unsized
53+
| ++++ +
54+
55+
error[E0277]: the size for values of type `str` cannot be known at compilation time
56+
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:37:17
57+
|
58+
LL | field2: str, // Unsized
59+
| ^^^ doesn't have a size known at compile-time
60+
|
61+
= help: the trait `Sized` is not implemented for `str`
62+
= note: no field of an enum variant may have a dynamically sized type
63+
= help: change the field's type to have a statically known size
64+
help: borrowed types always have a statically known size
65+
|
66+
LL | field2: &str, // Unsized
67+
| +
68+
help: the `Box` type always has a statically known size and allocates its contents in the heap
69+
|
70+
LL | field2: Box<str>, // Unsized
71+
| ++++ +
72+
73+
error[E0277]: the size for values of type `str` cannot be known at compilation time
74+
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:46:13
75+
|
76+
LL | field2: str, // Unsized
77+
| ^^^ doesn't have a size known at compile-time
78+
|
79+
= help: the trait `Sized` is not implemented for `str`
80+
= note: no field of a union may have a dynamically sized type
81+
= help: change the field's type to have a statically known size
82+
help: borrowed types always have a statically known size
83+
|
84+
LL | field2: &str, // Unsized
85+
| +
86+
help: the `Box` type always has a statically known size and allocates its contents in the heap
87+
|
88+
LL | field2: Box<str>, // Unsized
89+
| ++++ +
90+
91+
error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
92+
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:46:5
93+
|
94+
LL | field2: str, // Unsized
95+
| ^^^^^^^^^^^
96+
|
97+
= note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
98+
help: wrap the field type in `ManuallyDrop<...>`
99+
|
100+
LL | field2: std::mem::ManuallyDrop<str>, // Unsized
101+
| +++++++++++++++++++++++ +
102+
103+
error: aborting due to 6 previous errors
104+
105+
Some errors have detailed explanations: E0277, E0740.
106+
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)