Skip to content

Commit 0540a59

Browse files
committed
Check for unsized types in enums.
And allow the last field of a struct or variant to be unsized.
1 parent f78add1 commit 0540a59

File tree

7 files changed

+164
-16
lines changed

7 files changed

+164
-16
lines changed

src/librustc/middle/ty.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,10 @@ pub fn type_is_sized(cx: &ctxt, ty: ty::t) -> bool {
26082608
!tps.any(|ty| !type_is_sized(cx, ty))
26092609
}
26102610
ty_tup(ref ts) => !ts.iter().any(|t| !type_is_sized(cx, *t)),
2611+
ty_enum(did, ref substs) => {
2612+
let variants = substd_enum_variants(cx, did, substs);
2613+
!variants.iter().any(|v| v.args.iter().any(|t| !type_is_sized(cx, *t)))
2614+
}
26112615
_ => true
26122616
}
26132617
}

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

+66-16
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,23 @@ impl<'a> Visitor<()> for CheckItemTypesVisitor<'a> {
317317
}
318318
}
319319

320+
struct CheckItemSizedTypesVisitor<'a> { ccx: &'a CrateCtxt<'a> }
321+
322+
impl<'a> Visitor<()> for CheckItemSizedTypesVisitor<'a> {
323+
fn visit_item(&mut self, i: &ast::Item, _: ()) {
324+
check_item_sized(self.ccx, i);
325+
visit::walk_item(self, i, ());
326+
}
327+
}
328+
320329
pub fn check_item_types(ccx: &CrateCtxt, krate: &ast::Crate) {
321330
let mut visit = CheckItemTypesVisitor { ccx: ccx };
322331
visit::walk_crate(&mut visit, krate, ());
332+
333+
ccx.tcx.sess.abort_if_errors();
334+
335+
let mut visit = CheckItemSizedTypesVisitor { ccx: ccx };
336+
visit::walk_crate(&mut visit, krate, ());
323337
}
324338

325339
fn check_bare_fn(ccx: &CrateCtxt,
@@ -562,19 +576,19 @@ fn check_for_field_shadowing(tcx: &ty::ctxt,
562576
}
563577

564578
fn check_fields_sized(tcx: &ty::ctxt,
565-
id: ast::NodeId) {
566-
let struct_def = tcx.map.expect_struct(id);
567-
// FIXME(#13121) allow the last field to be DST
568-
for f in struct_def.fields.iter() {
579+
struct_def: @ast::StructDef) {
580+
let len = struct_def.fields.len();
581+
for i in range(0, len) {
582+
let f = struct_def.fields.get(i);
569583
let t = ty::node_id_to_type(tcx, f.node.id);
570-
if !ty::type_is_sized(tcx, t) {
584+
if !ty::type_is_sized(tcx, t) && i < (len - 1) {
571585
match f.node.kind {
572586
ast::NamedField(ident, _) => {
573-
tcx.sess.span_err(f.span, format!("Dynamically sized type in field {}",
587+
tcx.sess.span_err(f.span, format!("type of field {} is dynamically sized",
574588
token::get_ident(ident)));
575589
}
576590
ast::UnnamedField(_) => {
577-
tcx.sess.span_err(f.span, "Dynamically sized type in field");
591+
tcx.sess.span_err(f.span, "dynamically sized type in field");
578592
}
579593
}
580594
}
@@ -584,14 +598,8 @@ fn check_fields_sized(tcx: &ty::ctxt,
584598
pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
585599
let tcx = ccx.tcx;
586600

587-
// Check that the struct is representable
588601
check_representable(tcx, span, id, "struct");
589-
590-
// Check that the struct is instantiable
591-
if check_instantiable(tcx, span, id) {
592-
// This might cause stack overflow if id is not instantiable.
593-
check_fields_sized(tcx, id);
594-
}
602+
check_instantiable(tcx, span, id);
595603

596604
// Check there are no overlapping fields in super-structs
597605
check_for_field_shadowing(tcx, local_def(id));
@@ -601,6 +609,24 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
601609
}
602610
}
603611

612+
pub fn check_item_sized(ccx: &CrateCtxt, it: &ast::Item) {
613+
debug!("check_item(it.id={}, it.ident={})",
614+
it.id,
615+
ty::item_path_str(ccx.tcx, local_def(it.id)));
616+
let _indenter = indenter();
617+
618+
match it.node {
619+
ast::ItemEnum(ref enum_definition, _) => {
620+
check_enum_variants_sized(ccx,
621+
enum_definition.variants.as_slice());
622+
}
623+
ast::ItemStruct(..) => {
624+
check_fields_sized(ccx.tcx, ccx.tcx.map.expect_struct(it.id));
625+
}
626+
_ => {}
627+
}
628+
}
629+
604630
pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
605631
debug!("check_item(it.id={}, it.ident={})",
606632
it.id,
@@ -3459,7 +3485,7 @@ pub fn check_const_with_ty(fcx: &FnCtxt,
34593485
pub fn check_representable(tcx: &ty::ctxt,
34603486
sp: Span,
34613487
item_id: ast::NodeId,
3462-
designation: &str) {
3488+
designation: &str) -> bool {
34633489
let rty = ty::node_id_to_type(tcx, item_id);
34643490

34653491
// Check that it is possible to represent this type. This call identifies
@@ -3473,9 +3499,11 @@ pub fn check_representable(tcx: &ty::ctxt,
34733499
sp, format!("illegal recursive {} type; \
34743500
wrap the inner value in a box to make it representable",
34753501
designation));
3502+
return false
34763503
}
34773504
ty::Representable | ty::ContainsRecursive => (),
34783505
}
3506+
return true
34793507
}
34803508

34813509
/// Checks whether a type can be created without an instance of itself.
@@ -3532,6 +3560,29 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {
35323560
}
35333561
}
35343562

3563+
pub fn check_enum_variants_sized(ccx: &CrateCtxt,
3564+
vs: &[ast::P<ast::Variant>]) {
3565+
for &v in vs.iter() {
3566+
match v.node.kind {
3567+
ast::TupleVariantKind(ref args) if args.len() > 0 => {
3568+
let ctor_ty = ty::node_id_to_type(ccx.tcx, v.node.id);
3569+
let arg_tys: Vec<ty::t> = ty::ty_fn_args(ctor_ty).iter().map(|a| *a).collect();
3570+
for i in range(0, args.len()) {
3571+
let t = arg_tys.get(i);
3572+
// Allow the last field in an enum to be unsized.
3573+
if !ty::type_is_sized(ccx.tcx, *t) && i < args.len() -1 {
3574+
ccx.tcx.sess.span_err(args.get(i).ty.span,
3575+
format!("type {} is dynamically sized",
3576+
ppaux::ty_to_str(ccx.tcx, *t)));
3577+
}
3578+
}
3579+
},
3580+
ast::StructVariantKind(struct_def) => check_fields_sized(ccx.tcx, struct_def),
3581+
_ => {}
3582+
}
3583+
}
3584+
}
3585+
35353586
pub fn check_enum_variants(ccx: &CrateCtxt,
35363587
sp: Span,
35373588
vs: &[ast::P<ast::Variant>],
@@ -3652,7 +3703,6 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
36523703
// cache so that ty::enum_variants won't repeat this work
36533704
ccx.tcx.enum_var_cache.borrow_mut().insert(local_def(id), Rc::new(variants));
36543705

3655-
// Check that it is possible to represent this enum.
36563706
check_representable(ccx.tcx, sp, id, "enum");
36573707

36583708
// Check that it is possible to instantiate this enum:

src/libsyntax/ast_map.rs

+7
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,13 @@ impl Map {
283283
}
284284
}
285285

286+
pub fn expect_variant(&self, id: NodeId) -> P<Variant> {
287+
match self.find(id) {
288+
Some(NodeVariant(variant)) => variant,
289+
_ => fail!(format!("expected variant, found {}", self.node_to_str(id))),
290+
}
291+
}
292+
286293
pub fn expect_foreign_item(&self, id: NodeId) -> @ForeignItem {
287294
match self.find(id) {
288295
Some(NodeForeignItem(item)) => item,

src/test/compile-fail/unsized3.rs

+29
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,35 @@ fn f3<type X: T>(x: &X) {
2525
fn f4<X: T>(x: &X) {
2626
}
2727

28+
// Test with unsized enum.
29+
enum E<type X> {
30+
V(X),
31+
}
32+
33+
fn f5<Y>(x: &Y) {}
34+
fn f6<type X>(x: &X) {}
35+
fn f7<type X>(x1: &E<X>, x2: &E<X>) {
36+
f5(x1); //~ERROR instantiating a type parameter with an incompatible type `E<X>`, which does not
37+
f6(x2); // ok
38+
}
39+
40+
41+
// Test with unsized struct.
42+
struct S<type X> {
43+
x: X,
44+
}
45+
46+
fn f8<type X>(x1: &S<X>, x2: &S<X>) {
47+
f5(x1); //~ERROR instantiating a type parameter with an incompatible type `S<X>`, which does not
48+
f6(x2); // ok
49+
}
50+
51+
// Test some tuples.
52+
fn f9<type X>(x1: ~S<X>, x2: ~E<X>) {
53+
f5(&(*x1, 34)); //~ERROR instantiating a type parameter with an incompatible type `(S<X>,int)`,
54+
f5(&(32, *x2)); //~ERROR instantiating a type parameter with an incompatible type `(int,E<X>)`,
55+
}
56+
2857
// I would like these to fail eventually.
2958
/*
3059
// impl - bounded

src/test/compile-fail/unsized5.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![feature(struct_variant)]
11+
12+
// Test `type` types not allowed in fields or local variables.
13+
14+
/*trait T for type {}
15+
16+
fn f5<type X>(x: &X) {
17+
let _: X; // ERROR local variable with dynamically sized type X
18+
let _: (int, (X, int)); // ERROR local variable with dynamically sized type (int,(X,int))
19+
}
20+
fn f6<type X: T>(x: &X) {
21+
let _: X; // ERROR local variable with dynamically sized type X
22+
let _: (int, (X, int)); // ERROR local variable with dynamically sized type (int,(X,int))
23+
}*/
24+
25+
struct S1<type X> {
26+
f1: X, //~ ERROR type of field f1 is dynamically sized
27+
f2: int,
28+
}
29+
struct S2<type X> {
30+
f: int,
31+
g: X, //~ ERROR type of field g is dynamically sized
32+
h: int,
33+
}
34+
35+
enum E<type X> {
36+
V1(X, int), //~ERROR type X is dynamically sized
37+
V2{f1: X, f: int}, //~ERROR type of field f1 is dynamically sized
38+
}
39+
40+
pub fn main() {
41+
}

src/test/run-pass/unsized.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ trait T6<Y, type X> {}
1919
trait T7<type X, type Y> {}
2020
trait T8<type X: T2> {}
2121
struct S1<type X>;
22+
enum E<type X> {}
2223
impl <type X> T1 for S1<X> {}
2324
fn f<type X>() {}
2425

src/test/run-pass/unsized2.rs

+16
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
#![feature(struct_variant)]
1011

1112
// Test sized-ness checking in substitution.
1213

@@ -78,5 +79,20 @@ trait T7<type X: T> {
7879
fn m2(x: &T5<X>);
7980
}
8081

82+
// The last field in a struct or variant may be unsized
83+
struct S2<type X> {
84+
f: X,
85+
}
86+
struct S3<type X> {
87+
f1: int,
88+
f2: X,
89+
}
90+
enum E<type X> {
91+
V1(X),
92+
V2{x: X},
93+
V3(int, X),
94+
V4{u: int, x: X},
95+
}
96+
8197
pub fn main() {
8298
}

0 commit comments

Comments
 (0)