diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 49968de28da..7e0e4abef9d 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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 } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index cd880f80817..b4f22f3fa6e 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -317,9 +317,23 @@ impl<'a> Visitor<()> for CheckItemTypesVisitor<'a> { } } +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]) { + 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::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], @@ -3652,7 +3703,6 @@ pub fn check_enum_variants(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: diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index d0e3ff4ae54..7a167237d3e 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -283,6 +283,13 @@ impl Map { } } + pub fn expect_variant(&self, id: NodeId) -> P { + 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, diff --git a/src/test/compile-fail/unsized3.rs b/src/test/compile-fail/unsized3.rs index d842a289289..0ff5b1c9b5a 100644 --- a/src/test/compile-fail/unsized3.rs +++ b/src/test/compile-fail/unsized3.rs @@ -25,6 +25,35 @@ fn f3(x: &X) { fn f4(x: &X) { } +// Test with unsized enum. +enum E { + V(X), +} + +fn f5(x: &Y) {} +fn f6(x: &X) {} +fn f7(x1: &E, x2: &E) { + f5(x1); //~ERROR instantiating a type parameter with an incompatible type `E`, which does not + f6(x2); // ok +} + + +// Test with unsized struct. +struct S { + x: X, +} + +fn f8(x1: &S, x2: &S) { + f5(x1); //~ERROR instantiating a type parameter with an incompatible type `S`, which does not + f6(x2); // ok +} + +// Test some tuples. +fn f9(x1: ~S, x2: ~E) { + f5(&(*x1, 34)); //~ERROR instantiating a type parameter with an incompatible type `(S,int)`, + f5(&(32, *x2)); //~ERROR instantiating a type parameter with an incompatible type `(int,E)`, +} + // 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 index 00000000000..c3b50dcdcaf --- /dev/null +++ b/src/test/compile-fail/unsized5.rs @@ -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 or the MIT license +// , 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(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(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 { + f1: X, //~ ERROR type of field f1 is dynamically sized + f2: int, +} +struct S2 { + f: int, + g: X, //~ ERROR type of field g is dynamically sized + h: int, +} + +enum E { + 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() { +} diff --git a/src/test/run-pass/unsized.rs b/src/test/run-pass/unsized.rs index e7dadd4b5eb..db0cc83d786 100644 --- a/src/test/run-pass/unsized.rs +++ b/src/test/run-pass/unsized.rs @@ -19,6 +19,7 @@ trait T6 {} trait T7 {} trait T8 {} struct S1; +enum E {} impl T1 for S1 {} fn f() {} diff --git a/src/test/run-pass/unsized2.rs b/src/test/run-pass/unsized2.rs index c8e8f98145f..7cc2dfd816f 100644 --- a/src/test/run-pass/unsized2.rs +++ b/src/test/run-pass/unsized2.rs @@ -7,6 +7,7 @@ // , 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 { fn m2(x: &T5); } +// The last field in a struct or variant may be unsized +struct S2 { + f: X, +} +struct S3 { + f1: int, + f2: X, +} +enum E { + V1(X), + V2{x: X}, + V3(int, X), + V4{u: int, x: X}, +} + pub fn main() { }