Skip to content

Commit 1839d5d

Browse files
committed
Forbid unsized structs which are non-instantiable
Closes #16977
1 parent d7502ac commit 1839d5d

File tree

4 files changed

+82
-12
lines changed

4 files changed

+82
-12
lines changed

src/librustc/diagnostics.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -169,5 +169,7 @@ register_diagnostics!(
169169
E0157,
170170
E0158,
171171
E0159,
172-
E0160
172+
E0160,
173+
E0162,
174+
E0163
173175
)

src/librustc/middle/typeck/check/mod.rs

+57-6
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,40 @@ fn check_fields_sized(tcx: &ty::ctxt,
706706
}
707707
}
708708

709+
// Checks that the type ty can possibly be made sized. That means there exists
710+
// some set of type parameters such that ty::type_is_sized is true.
711+
// For enums, ty may be either the type of the enum itself, or the constructor
712+
// type for any of its variants.
713+
fn check_ty_may_be_sized(tcx: &ty::ctxt,
714+
ty: ty::t) -> bool {
715+
let t = sized_ty(ty, tcx);
716+
return ty::type_is_sized(tcx, t);
717+
718+
// Finds a type to be tested for sizedness for a given input type. This will
719+
// replaces any type parameters with sized ones, if given an enum variant's
720+
// constructor, will return the type for the enum itself.
721+
fn sized_ty(ty: ty::t, tcx: &ty::ctxt) -> ty::t {
722+
// For a given set Substs, returns a new Substs where all actual type
723+
// parameters are sized.
724+
fn sized_substs(substs: &subst::Substs) -> subst::Substs {
725+
let mut substs = substs.clone();
726+
let tys = Vec::from_fn(substs.types.get_slice(subst::TypeSpace).len(),
727+
|_| ty::mk_bot());
728+
substs.types.replace(subst::TypeSpace, tys);
729+
substs
730+
}
731+
732+
match ty::get(ty).sty {
733+
ty::ty_enum(id, ref substs) => ty::mk_enum(tcx, id, sized_substs(substs)),
734+
ty::ty_struct(id, ref substs) => ty::mk_struct(tcx, id, sized_substs(substs)),
735+
ty::ty_bare_fn(ref f) => sized_ty(f.sig.output, tcx),
736+
_ => tcx.sess.bug(format!("Unexpected type: {} in check_ty_may_be_sized",
737+
ppaux::ty_to_string(tcx, ty)).as_slice())
738+
}
739+
}
740+
741+
}
742+
709743
pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
710744
let tcx = ccx.tcx;
711745

@@ -793,11 +827,19 @@ pub fn check_item_sized(ccx: &CrateCtxt, it: &ast::Item) {
793827

794828
match it.node {
795829
ast::ItemEnum(ref enum_definition, _) => {
796-
check_enum_variants_sized(ccx,
797-
enum_definition.variants.as_slice());
830+
if !check_enum_variants_sized(ccx, enum_definition.variants.as_slice()) {
831+
span_err!(ccx.tcx.sess, it.span, E0162,
832+
"enum cannot be instantiated since it can never have a statically known size");
833+
}
798834
}
799835
ast::ItemStruct(..) => {
800-
check_fields_sized(ccx.tcx, &*ccx.tcx.map.expect_struct(it.id));
836+
check_fields_sized(ccx.tcx, &*ccx.tcx.map.expect_struct(it.id),);
837+
838+
if !check_ty_may_be_sized(ccx.tcx, ty::node_id_to_type(ccx.tcx, it.id)) {
839+
span_err!(ccx.tcx.sess, it.span, E0163,
840+
"struct cannot be instantiated since \
841+
it can never have a statically known size");
842+
}
801843
}
802844
_ => {}
803845
}
@@ -4538,15 +4580,16 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {
45384580

45394581

45404582
pub fn check_enum_variants_sized(ccx: &CrateCtxt,
4541-
vs: &[ast::P<ast::Variant>]) {
4583+
vs: &[ast::P<ast::Variant>]) -> bool {
4584+
let mut sized_ok = true;
45424585
for &v in vs.iter() {
45434586
match v.node.kind {
45444587
ast::TupleVariantKind(ref args) if args.len() > 0 => {
45454588
let ctor_ty = ty::node_id_to_type(ccx.tcx, v.node.id);
45464589
let arg_tys: Vec<ty::t> = ty::ty_fn_args(ctor_ty).iter().map(|a| *a).collect();
45474590
let len = arg_tys.len();
45484591
if len == 0 {
4549-
return;
4592+
continue;
45504593
}
45514594
for (i, t) in arg_tys.slice_to(len - 1).iter().enumerate() {
45524595
// Allow the last field in an enum to be unsized.
@@ -4560,11 +4603,19 @@ pub fn check_enum_variants_sized(ccx: &CrateCtxt,
45604603
ppaux::ty_to_string(ccx.tcx, *t));
45614604
}
45624605
}
4606+
4607+
sized_ok = sized_ok && check_ty_may_be_sized(ccx.tcx, ctor_ty);
45634608
},
4564-
ast::StructVariantKind(struct_def) => check_fields_sized(ccx.tcx, &*struct_def),
4609+
ast::StructVariantKind(struct_def) => {
4610+
check_fields_sized(ccx.tcx, &*struct_def);
4611+
sized_ok = sized_ok && check_ty_may_be_sized(ccx.tcx,
4612+
ty::node_id_to_type(ccx.tcx,
4613+
v.node.id))
4614+
}
45654615
_ => {}
45664616
}
45674617
}
4618+
sized_ok
45684619
}
45694620

45704621
pub fn check_enum_variants(ccx: &CrateCtxt,

src/test/compile-fail/issue-5883.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010

1111
trait A {}
1212

13-
struct Struct {
13+
struct Struct { //~ ERROR struct cannot be instantiated
1414
r: A+'static
1515
}
1616

1717
fn new_struct(r: A+'static) -> Struct {
18-
//~^ ERROR variable `r` has dynamically sized type
19-
Struct { r: r } //~ ERROR trying to initialise a dynamically sized struct
18+
Struct { r: r }
2019
}
2120

2221
trait Curve {}
23-
enum E {X(Curve+'static)}
22+
enum E {X(Curve+'static)} //~ ERROR enum cannot be instantiated
2423
fn main() {}

src/test/compile-fail/unsized5.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,28 @@ struct S2<Sized? X> {
2121
h: int,
2222
}
2323

24-
enum E<Sized? X> {
24+
enum E1<Sized? X> {
2525
V1(X, int), //~ERROR type `X` is dynamically sized. dynamically sized types may only appear as t
2626
V2{f1: X, f: int}, //~ERROR type `f1` is dynamically sized. dynamically sized types may only app
2727
}
2828

29+
// Structs/enums with unsized fields must still be instantiable.
30+
31+
struct S3 { //~ ERROR struct cannot be instantiated
32+
f: [int]
33+
}
34+
35+
trait T {}
36+
struct S4<X: T> { //~ ERROR struct cannot be instantiated
37+
f1: Box<X>,
38+
f2: [X]
39+
}
40+
41+
enum E2<Sized? X> { //~ ERROR enum cannot be instantiated
42+
V3(int),
43+
V4([X]),
44+
V5{f: [X]}
45+
}
46+
2947
pub fn main() {
3048
}

0 commit comments

Comments
 (0)