]> git.lizzy.rs Git - rust.git/commitdiff
Check for unsized types in enums.
authorNick Cameron <ncameron@mozilla.com>
Tue, 8 Apr 2014 09:00:20 +0000 (21:00 +1200)
committerNick Cameron <ncameron@mozilla.com>
Wed, 23 Apr 2014 00:30:58 +0000 (12:30 +1200)
And allow the last field of a struct or variant to be unsized.

src/librustc/middle/ty.rs
src/librustc/middle/typeck/check/mod.rs
src/libsyntax/ast_map.rs
src/test/compile-fail/unsized3.rs
src/test/compile-fail/unsized5.rs [new file with mode: 0644]
src/test/run-pass/unsized.rs
src/test/run-pass/unsized2.rs

index 49968de28daf532d112b0dfac1e4880864e651ae..7e0e4abef9d35706d7f375c54116178731466a85 100644 (file)
@@ -2608,6 +2608,10 @@ pub fn type_is_sized(cx: &ctxt, ty: ty::t) -> bool {
             !tps.any(|ty| !type_is_sized(cx, ty))
         }
         ty_tup(ref ts) => !ts.iter().any(|t| !type_is_sized(cx, *t)),
+        ty_enum(did, ref substs) => {
+            let variants = substd_enum_variants(cx, did, substs);
+            !variants.iter().any(|v| v.args.iter().any(|t| !type_is_sized(cx, *t)))
+        }
         _ => true
     }
 }
index cd880f8081729325865a12e65290c014e46998aa..b4f22f3fa6ed67b55f71c09218861badb0a4a812 100644 (file)
@@ -317,9 +317,23 @@ fn visit_item(&mut self, i: &ast::Item, _: ()) {
     }
 }
 
+struct CheckItemSizedTypesVisitor<'a> { ccx: &'a CrateCtxt<'a> }
+
+impl<'a> Visitor<()> for CheckItemSizedTypesVisitor<'a> {
+    fn visit_item(&mut self, i: &ast::Item, _: ()) {
+        check_item_sized(self.ccx, i);
+        visit::walk_item(self, i, ());
+    }
+}
+
 pub fn check_item_types(ccx: &CrateCtxt, krate: &ast::Crate) {
     let mut visit = CheckItemTypesVisitor { ccx: ccx };
     visit::walk_crate(&mut visit, krate, ());
+
+    ccx.tcx.sess.abort_if_errors();
+
+    let mut visit = CheckItemSizedTypesVisitor { ccx: ccx };
+    visit::walk_crate(&mut visit, krate, ());
 }
 
 fn check_bare_fn(ccx: &CrateCtxt,
@@ -562,19 +576,19 @@ fn check_for_field_shadowing(tcx: &ty::ctxt,
 }
 
 fn check_fields_sized(tcx: &ty::ctxt,
-                      id: ast::NodeId) {
-    let struct_def = tcx.map.expect_struct(id);
-    // FIXME(#13121) allow the last field to be DST
-    for f in struct_def.fields.iter() {
+                      struct_def: @ast::StructDef) {
+    let len = struct_def.fields.len();
+    for i in range(0, len) {
+        let f = struct_def.fields.get(i);
         let t = ty::node_id_to_type(tcx, f.node.id);
-        if !ty::type_is_sized(tcx, t) {
+        if !ty::type_is_sized(tcx, t) && i < (len - 1) {
             match f.node.kind {
                 ast::NamedField(ident, _) => {
-                    tcx.sess.span_err(f.span, format!("Dynamically sized type in field {}",
+                    tcx.sess.span_err(f.span, format!("type of field {} is dynamically sized",
                                                       token::get_ident(ident)));
                 }
                 ast::UnnamedField(_) => {
-                    tcx.sess.span_err(f.span, "Dynamically sized type in field");
+                    tcx.sess.span_err(f.span, "dynamically sized type in field");
                 }
             }
         }
@@ -584,14 +598,8 @@ fn check_fields_sized(tcx: &ty::ctxt,
 pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
     let tcx = ccx.tcx;
 
-    // Check that the struct is representable
     check_representable(tcx, span, id, "struct");
-
-    // Check that the struct is instantiable
-    if check_instantiable(tcx, span, id) {
-        // This might cause stack overflow if id is not instantiable.
-        check_fields_sized(tcx, id);
-    }
+    check_instantiable(tcx, span, id);
 
     // Check there are no overlapping fields in super-structs
     check_for_field_shadowing(tcx, local_def(id));
@@ -601,6 +609,24 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
     }
 }
 
+pub fn check_item_sized(ccx: &CrateCtxt, it: &ast::Item) {
+    debug!("check_item(it.id={}, it.ident={})",
+           it.id,
+           ty::item_path_str(ccx.tcx, local_def(it.id)));
+    let _indenter = indenter();
+
+    match it.node {
+        ast::ItemEnum(ref enum_definition, _) => {
+            check_enum_variants_sized(ccx,
+                                      enum_definition.variants.as_slice());
+        }
+        ast::ItemStruct(..) => {
+            check_fields_sized(ccx.tcx, ccx.tcx.map.expect_struct(it.id));
+        }
+        _ => {}
+    }
+}
+
 pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) {
     debug!("check_item(it.id={}, it.ident={})",
            it.id,
@@ -3459,7 +3485,7 @@ pub fn check_const_with_ty(fcx: &FnCtxt,
 pub fn check_representable(tcx: &ty::ctxt,
                            sp: Span,
                            item_id: ast::NodeId,
-                           designation: &str) {
+                           designation: &str) -> bool {
     let rty = ty::node_id_to_type(tcx, item_id);
 
     // Check that it is possible to represent this type. This call identifies
@@ -3473,9 +3499,11 @@ pub fn check_representable(tcx: &ty::ctxt,
           sp, format!("illegal recursive {} type; \
                        wrap the inner value in a box to make it representable",
                       designation));
+        return false
       }
       ty::Representable | ty::ContainsRecursive => (),
     }
+    return true
 }
 
 /// 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) {
     }
 }
 
+pub fn check_enum_variants_sized(ccx: &CrateCtxt,
+                                 vs: &[ast::P<ast::Variant>]) {
+    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();
+                for i in range(0, args.len()) {
+                    let t = arg_tys.get(i);
+                    // Allow the last field in an enum to be unsized.
+                    if !ty::type_is_sized(ccx.tcx, *t) && i < args.len() -1 {
+                        ccx.tcx.sess.span_err(args.get(i).ty.span,
+                                              format!("type {} is dynamically sized",
+                                                      ppaux::ty_to_str(ccx.tcx, *t)));
+                    }
+                }
+            },
+            ast::StructVariantKind(struct_def) => check_fields_sized(ccx.tcx, struct_def),
+            _ => {}
+        }
+    }
+}
+
 pub fn check_enum_variants(ccx: &CrateCtxt,
                            sp: Span,
                            vs: &[ast::P<ast::Variant>],
@@ -3652,7 +3703,6 @@ fn do_check(ccx: &CrateCtxt,
     // cache so that ty::enum_variants won't repeat this work
     ccx.tcx.enum_var_cache.borrow_mut().insert(local_def(id), Rc::new(variants));
 
-    // Check that it is possible to represent this enum.
     check_representable(ccx.tcx, sp, id, "enum");
 
     // Check that it is possible to instantiate this enum:
index d0e3ff4ae546ad31fe8249c6a240caf120635132..7a167237d3e5db38f8a8d1adcbef41dd24d58835 100644 (file)
@@ -283,6 +283,13 @@ pub fn expect_struct(&self, id: NodeId) -> @StructDef {
         }
     }
 
+    pub fn expect_variant(&self, id: NodeId) -> P<Variant> {
+        match self.find(id) {
+            Some(NodeVariant(variant)) => variant,
+            _ => fail!(format!("expected variant, found {}", self.node_to_str(id))),
+        }
+    }
+
     pub fn expect_foreign_item(&self, id: NodeId) -> @ForeignItem {
         match self.find(id) {
             Some(NodeForeignItem(item)) => item,
index d842a28928927ddb012abe318f4af68de851b817..0ff5b1c9b5ad8b76c1ca08b67e031567ac4153a8 100644 (file)
@@ -25,6 +25,35 @@ fn f3<type X: T>(x: &X) {
 fn f4<X: T>(x: &X) {
 }
 
+// Test with unsized enum.
+enum E<type X> {
+    V(X),
+}
+
+fn f5<Y>(x: &Y) {}
+fn f6<type X>(x: &X) {}
+fn f7<type X>(x1: &E<X>, x2: &E<X>) {
+    f5(x1); //~ERROR instantiating a type parameter with an incompatible type `E<X>`, which does not
+    f6(x2); // ok
+}
+
+
+// Test with unsized struct.
+struct S<type X> {
+    x: X,
+}
+
+fn f8<type X>(x1: &S<X>, x2: &S<X>) {
+    f5(x1); //~ERROR instantiating a type parameter with an incompatible type `S<X>`, which does not
+    f6(x2); // ok
+}
+
+// Test some tuples.
+fn f9<type X>(x1: ~S<X>, x2: ~E<X>) {
+    f5(&(*x1, 34)); //~ERROR instantiating a type parameter with an incompatible type `(S<X>,int)`,
+    f5(&(32, *x2)); //~ERROR instantiating a type parameter with an incompatible type `(int,E<X>)`,
+}
+
 // I would like these to fail eventually.
 /*
 // impl - bounded
diff --git a/src/test/compile-fail/unsized5.rs b/src/test/compile-fail/unsized5.rs
new file mode 100644 (file)
index 0000000..c3b50dc
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#![feature(struct_variant)]
+
+// Test `type` types not allowed in fields or local variables.
+
+/*trait T for type {}
+
+fn f5<type X>(x: &X) {
+    let _: X; // ERROR local variable with dynamically sized type X
+    let _: (int, (X, int)); // ERROR local variable with dynamically sized type (int,(X,int))
+}
+fn f6<type X: T>(x: &X) {
+    let _: X; // ERROR local variable with dynamically sized type X
+    let _: (int, (X, int)); // ERROR local variable with dynamically sized type (int,(X,int))
+}*/
+
+struct S1<type X> {
+    f1: X, //~ ERROR type of field f1 is dynamically sized
+    f2: int,
+}
+struct S2<type X> {
+    f: int,
+    g: X, //~ ERROR type of field g is dynamically sized
+    h: int,
+}
+
+enum E<type X> {
+    V1(X, int), //~ERROR type X is dynamically sized
+    V2{f1: X, f: int}, //~ERROR type of field f1 is dynamically sized
+}
+
+pub fn main() {
+}
index e7dadd4b5eb4f7931d5fe08efb4e0732e8730517..db0cc83d7866af826dcdadb3602850083b1b822c 100644 (file)
@@ -19,6 +19,7 @@ trait T6<Y, type X> {}
 trait T7<type X, type Y> {}
 trait T8<type X: T2> {}
 struct S1<type X>;
+enum E<type X> {}
 impl <type X> T1 for S1<X> {}
 fn f<type X>() {}
 
index c8e8f98145fe8293039ca1b4e24cdb44056736ba..7cc2dfd816f358dca7af5f2393419daaa854d597 100644 (file)
@@ -7,6 +7,7 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+#![feature(struct_variant)]
 
 // Test sized-ness checking in substitution.
 
@@ -78,5 +79,20 @@ trait T7<type X: T> {
     fn m2(x: &T5<X>);
 }
 
+// The last field in a struct or variant may be unsized
+struct S2<type X> {
+    f: X,
+}
+struct S3<type X> {
+    f1: int,
+    f2: X,
+}
+enum E<type X> {
+    V1(X),
+    V2{x: X},
+    V3(int, X),
+    V4{u: int, x: X},
+}
+
 pub fn main() {
 }