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 337a1b7

Browse files
committedApr 4, 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 3d5528c commit 337a1b7

File tree

3 files changed

+195
-1
lines changed

3 files changed

+195
-1
lines changed
 

‎compiler/rustc_ty_utils/src/layout.rs

+42-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,45 @@ 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+
538+
if def.is_enum() {
539+
for f in def.all_fields() {
540+
if is_unsized_field(f)? {
541+
cx.tcx.dcx().span_delayed_bug(
542+
tcx.def_span(def.did()),
543+
"no field of an enum can be unsized",
544+
);
545+
return Err(error(cx, LayoutError::Unknown(ty)));
546+
}
547+
}
548+
}
549+
509550
let get_discriminant_type =
510551
|min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
511552

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
union BadUnion {
34+
field1: i32,
35+
field2: str, // Unsized
36+
//~^ ERROR the size for values of type `str` cannot be known at compilation time
37+
//~| ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
38+
field3: [u8; 16],
39+
}
40+
41+
// Used to test that projection type fields that normalize
42+
// to a sized type do not cause problems
43+
struct StructWithProjections<'a>
44+
{
45+
field1: <&'a [i32] as IntoIterator>::IntoIter,
46+
field2: i32
47+
}
48+
49+
pub fn main() {
50+
let _a = &size_of::<BadStruct>();
51+
assert_eq!(size_of::<BadStruct>(), 21);
52+
53+
let _a = &size_of::<BadEnum1>();
54+
assert_eq!(size_of::<BadEnum1>(), 21);
55+
56+
let _a = &size_of::<BadEnum2>();
57+
assert_eq!(size_of::<BadEnum2>(), 21);
58+
59+
let _a = &size_of::<BadUnion>();
60+
assert_eq!(size_of::<BadUnion>(), 21);
61+
62+
let _a = &size_of::<StructWithProjections>();
63+
assert_eq!(size_of::<StructWithProjections>(), 21);
64+
let _a = StructWithProjections { field1: [1, 3].iter(), field2: 3 };
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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:35:13
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 a union 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[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union
74+
--> $DIR/ice-non-last-unsized-field-issue-121473.rs:35:5
75+
|
76+
LL | field2: str, // Unsized
77+
| ^^^^^^^^^^^
78+
|
79+
= note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
80+
help: wrap the field type in `ManuallyDrop<...>`
81+
|
82+
LL | field2: std::mem::ManuallyDrop<str>, // Unsized
83+
| +++++++++++++++++++++++ +
84+
85+
error: aborting due to 5 previous errors
86+
87+
Some errors have detailed explanations: E0277, E0740.
88+
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)
Please sign in to comment.