Skip to content

Forbid unsized structs which are non-instantiable #16985

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,7 @@ register_diagnostics!(
E0157,
E0158,
E0159,
E0160
E0160,
E0162,
E0163
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you skip E0161?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I added it in a different PR and it will make rebasing easier :-)

)
63 changes: 57 additions & 6 deletions src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,40 @@ fn check_fields_sized(tcx: &ty::ctxt,
}
}

// Checks that the type ty can possibly be made sized. That means there exists
// some set of type parameters such that ty::type_is_sized is true.
// For enums, ty may be either the type of the enum itself, or the constructor
// type for any of its variants.
fn check_ty_may_be_sized(tcx: &ty::ctxt,
ty: ty::t) -> bool {
let t = sized_ty(ty, tcx);
return ty::type_is_sized(tcx, t);

// Finds a type to be tested for sizedness for a given input type. This will
// replaces any type parameters with sized ones, if given an enum variant's
// constructor, will return the type for the enum itself.
fn sized_ty(ty: ty::t, tcx: &ty::ctxt) -> ty::t {
// For a given set Substs, returns a new Substs where all actual type
// parameters are sized.
fn sized_substs(substs: &subst::Substs) -> subst::Substs {
let mut substs = substs.clone();
let tys = Vec::from_fn(substs.types.get_slice(subst::TypeSpace).len(),
|_| ty::mk_bot());
substs.types.replace(subst::TypeSpace, tys);
substs
}

match ty::get(ty).sty {
ty::ty_enum(id, ref substs) => ty::mk_enum(tcx, id, sized_substs(substs)),
ty::ty_struct(id, ref substs) => ty::mk_struct(tcx, id, sized_substs(substs)),
ty::ty_bare_fn(ref f) => sized_ty(f.sig.output, tcx),
_ => tcx.sess.bug(format!("Unexpected type: {} in check_ty_may_be_sized",
ppaux::ty_to_string(tcx, ty)).as_slice())
}
}

}

pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
let tcx = ccx.tcx;

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

match it.node {
ast::ItemEnum(ref enum_definition, _) => {
check_enum_variants_sized(ccx,
enum_definition.variants.as_slice());
if !check_enum_variants_sized(ccx, enum_definition.variants.as_slice()) {
span_err!(ccx.tcx.sess, it.span, E0162,
"enum cannot be instantiated since it can never have a statically known size");
}
}
ast::ItemStruct(..) => {
check_fields_sized(ccx.tcx, &*ccx.tcx.map.expect_struct(it.id));
check_fields_sized(ccx.tcx, &*ccx.tcx.map.expect_struct(it.id),);

if !check_ty_may_be_sized(ccx.tcx, ty::node_id_to_type(ccx.tcx, it.id)) {
span_err!(ccx.tcx.sess, it.span, E0163,
"struct cannot be instantiated since \
it can never have a statically known size");
}
}
_ => {}
}
Expand Down Expand Up @@ -4538,15 +4580,16 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {


pub fn check_enum_variants_sized(ccx: &CrateCtxt,
vs: &[ast::P<ast::Variant>]) {
vs: &[ast::P<ast::Variant>]) -> bool {
let mut sized_ok = true;
for &v in vs.iter() {
match v.node.kind {
ast::TupleVariantKind(ref args) if args.len() > 0 => {
let ctor_ty = ty::node_id_to_type(ccx.tcx, v.node.id);
let arg_tys: Vec<ty::t> = ty::ty_fn_args(ctor_ty).iter().map(|a| *a).collect();
let len = arg_tys.len();
if len == 0 {
return;
continue;
}
for (i, t) in arg_tys.slice_to(len - 1).iter().enumerate() {
// Allow the last field in an enum to be unsized.
Expand All @@ -4560,11 +4603,19 @@ pub fn check_enum_variants_sized(ccx: &CrateCtxt,
ppaux::ty_to_string(ccx.tcx, *t));
}
}

sized_ok = sized_ok && check_ty_may_be_sized(ccx.tcx, ctor_ty);
},
ast::StructVariantKind(struct_def) => check_fields_sized(ccx.tcx, &*struct_def),
ast::StructVariantKind(struct_def) => {
check_fields_sized(ccx.tcx, &*struct_def);
sized_ok = sized_ok && check_ty_may_be_sized(ccx.tcx,
ty::node_id_to_type(ccx.tcx,
v.node.id))
}
_ => {}
}
}
sized_ok
}

pub fn check_enum_variants(ccx: &CrateCtxt,
Expand Down
7 changes: 3 additions & 4 deletions src/test/compile-fail/issue-5883.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@

trait A {}

struct Struct {
struct Struct { //~ ERROR struct cannot be instantiated
r: A+'static
}

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

trait Curve {}
enum E {X(Curve+'static)}
enum E {X(Curve+'static)} //~ ERROR enum cannot be instantiated
fn main() {}
20 changes: 19 additions & 1 deletion src/test/compile-fail/unsized5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,28 @@ struct S2<Sized? X> {
h: int,
}

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

// Structs/enums with unsized fields must still be instantiable.

struct S3 { //~ ERROR struct cannot be instantiated
f: [int]
}

trait T {}
struct S4<X: T> { //~ ERROR struct cannot be instantiated
f1: Box<X>,
f2: [X]
}

enum E2<Sized? X> { //~ ERROR enum cannot be instantiated
V3(int),
V4([X]),
V5{f: [X]}
}

pub fn main() {
}