From c3b2f2b0c6f074fb98add56a1977d407e294c9ed Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 3 Apr 2014 13:53:57 +1300 Subject: [PATCH 1/4] Add a span to ast::TyParam --- src/libsyntax/ast.rs | 3 ++- src/libsyntax/ext/build.rs | 5 ++++- src/libsyntax/ext/deriving/generic.rs | 2 +- src/libsyntax/ext/deriving/ty.rs | 2 +- src/libsyntax/fold.rs | 3 ++- src/libsyntax/parse/parser.rs | 4 +++- 6 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 2b6f94e6bf5..5d0b24fdb3e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -181,7 +181,8 @@ pub struct TyParam { pub ident: Ident, pub id: NodeId, pub bounds: OwnedSlice, - pub default: Option> + pub default: Option>, + pub span: Span } #[deriving(Clone, Eq, TotalEq, Encodable, Decodable, Hash)] diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 203edf6590f..c1289ef9858 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -66,6 +66,7 @@ pub trait AstBuilder { fn strip_bounds(&self, bounds: &Generics) -> Generics; fn typaram(&self, + span: Span, id: ast::Ident, bounds: OwnedSlice, default: Option>) -> ast::TyParam; @@ -368,6 +369,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { } fn typaram(&self, + span: Span, id: ast::Ident, bounds: OwnedSlice, default: Option>) -> ast::TyParam { @@ -375,7 +377,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> { ident: id, id: ast::DUMMY_NODE_ID, bounds: bounds, - default: default + default: default, + span: span } } diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 1d4aa08f9e3..c040361a8eb 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -380,7 +380,7 @@ impl<'a> TraitDef<'a> { // require the current trait bounds.push(cx.typarambound(trait_path.clone())); - cx.typaram(ty_param.ident, OwnedSlice::from_vec(bounds), None) + cx.typaram(self.span, ty_param.ident, OwnedSlice::from_vec(bounds), None) })); let trait_generics = Generics { lifetimes: lifetimes, diff --git a/src/libsyntax/ext/deriving/ty.rs b/src/libsyntax/ext/deriving/ty.rs index c4ca2601f60..a6bbad62b8e 100644 --- a/src/libsyntax/ext/deriving/ty.rs +++ b/src/libsyntax/ext/deriving/ty.rs @@ -193,7 +193,7 @@ fn mk_ty_param(cx: &ExtCtxt, span: Span, name: &str, bounds: &[Path], let path = b.to_path(cx, span, self_ident, self_generics); cx.typarambound(path) }).collect(); - cx.typaram(cx.ident_of(name), bounds, None) + cx.typaram(span, cx.ident_of(name), bounds, None) } fn mk_generics(lifetimes: Vec , ty_params: Vec ) -> Generics { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 73ad2664be4..72be633d456 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -448,7 +448,8 @@ pub fn fold_ty_param(tp: &TyParam, fld: &mut T) -> TyParam { ident: tp.ident, id: id, bounds: tp.bounds.map(|x| fold_ty_param_bound(x, fld)), - default: tp.default.map(|x| fld.fold_ty(x)) + default: tp.default.map(|x| fld.fold_ty(x)), + span: tp.span } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6485b5a3622..85480bebc90 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3393,6 +3393,7 @@ impl<'a> Parser<'a> { // matches typaram = IDENT optbounds ( EQ ty )? fn parse_ty_param(&mut self) -> TyParam { let ident = self.parse_ident(); + let span = self.span; let (_, opt_bounds) = self.parse_optional_ty_param_bounds(false); // For typarams we don't care about the difference b/w "" and "". let bounds = opt_bounds.unwrap_or_default(); @@ -3407,7 +3408,8 @@ impl<'a> Parser<'a> { ident: ident, id: ast::DUMMY_NODE_ID, bounds: bounds, - default: default + default: default, + span: span, } } From f78add10cde772b9d743a84a604dc584b63a06fc Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 3 Apr 2014 13:38:45 +1300 Subject: [PATCH 2/4] Support unsized types with the `type` keyword --- src/librustc/front/config.rs | 4 +- src/librustc/metadata/common.rs | 1 + src/librustc/metadata/decoder.rs | 21 +++++ src/librustc/metadata/encoder.rs | 15 +++- src/librustc/middle/privacy.rs | 8 +- src/librustc/middle/resolve.rs | 7 +- src/librustc/middle/resolve_lifetime.rs | 2 +- src/librustc/middle/ty.rs | 26 ++++-- src/librustc/middle/typeck/check/mod.rs | 57 +++++++++---- src/librustc/middle/typeck/collect.rs | 76 ++++++++++++++--- .../middle/typeck/infer/error_reporting.rs | 2 + src/librustc/middle/typeck/variance.rs | 2 +- src/librustdoc/visit_ast.rs | 2 +- src/libsyntax/ast.rs | 9 +- src/libsyntax/ast_map.rs | 20 ++++- src/libsyntax/ext/build.rs | 3 + src/libsyntax/ext/deriving/decodable.rs | 5 +- src/libsyntax/ext/deriving/encodable.rs | 5 +- src/libsyntax/ext/deriving/generic.rs | 6 +- src/libsyntax/ext/deriving/hash.rs | 3 +- src/libsyntax/ext/deriving/rand.rs | 3 +- src/libsyntax/ext/deriving/ty.rs | 9 +- src/libsyntax/fold.rs | 4 +- src/libsyntax/parse/parser.rs | 27 +++++- src/libsyntax/print/pprust.rs | 8 +- src/libsyntax/visit.rs | 2 +- .../compile-fail/bad-mid-path-type-params.rs | 3 + .../borrowck-move-subcomponent.rs | 3 + src/test/compile-fail/lint-dead-code-1.rs | 4 + src/test/compile-fail/privacy1.rs | 3 + src/test/compile-fail/unsized-bare-typaram.rs | 2 +- src/test/compile-fail/unsized-enum.rs | 2 +- src/test/compile-fail/unsized-struct.rs | 2 +- src/test/compile-fail/unsized.rs | 16 ++++ src/test/compile-fail/unsized2.rs | 17 ++++ src/test/compile-fail/unsized3.rs | 51 ++++++++++++ src/test/compile-fail/unsized4.rs | 19 +++++ src/test/run-pass/unsized.rs | 26 ++++++ src/test/run-pass/unsized2.rs | 82 +++++++++++++++++++ 39 files changed, 484 insertions(+), 73 deletions(-) create mode 100644 src/test/compile-fail/unsized.rs create mode 100644 src/test/compile-fail/unsized2.rs create mode 100644 src/test/compile-fail/unsized3.rs create mode 100644 src/test/compile-fail/unsized4.rs create mode 100644 src/test/run-pass/unsized.rs create mode 100644 src/test/run-pass/unsized2.rs diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs index e630d7e15e0..b8f20b5e439 100644 --- a/src/librustc/front/config.rs +++ b/src/librustc/front/config.rs @@ -103,12 +103,12 @@ fn fold_item_underscore(cx: &mut Context, item: &ast::Item_) -> ast::Item_ { .map(|x| *x).collect(); ast::ItemImpl((*a).clone(), (*b).clone(), c, methods) } - ast::ItemTrait(ref a, ref b, ref methods) => { + ast::ItemTrait(ref a, b, ref c, ref methods) => { let methods = methods.iter() .filter(|m| trait_method_in_cfg(cx, *m) ) .map(|x| (*x).clone()) .collect(); - ast::ItemTrait((*a).clone(), (*b).clone(), methods) + ast::ItemTrait((*a).clone(), b, (*c).clone(), methods) } ast::ItemStruct(def, ref generics) => { ast::ItemStruct(fold_struct(cx, def), generics.clone()) diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 4a0b3ea0cf6..0ea6598a99f 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -170,6 +170,7 @@ pub static tag_lang_items_item_node_id: uint = 0x73; pub static tag_item_unnamed_field: uint = 0x74; pub static tag_items_data_item_visibility: uint = 0x76; +pub static tag_items_data_item_sized: uint = 0x77; pub static tag_item_method_tps: uint = 0x79; pub static tag_item_method_fty: uint = 0x7a; diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 37d9d3417c9..b76b6d0c380 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -164,6 +164,19 @@ fn item_visibility(item: ebml::Doc) -> ast::Visibility { } } +fn item_sized(item: ebml::Doc) -> ast::Sized { + match reader::maybe_get_doc(item, tag_items_data_item_sized) { + None => ast::StaticSize, + Some(sized_doc) => { + match reader::doc_as_u8(sized_doc) as char { + 'd' => ast::DynSize, + 's' => ast::StaticSize, + _ => fail!("unknown sized-ness character") + } + } + } +} + fn item_method_sort(item: ebml::Doc) -> char { let mut ret = 'r'; reader::tagged_docs(item, tag_item_trait_method_sort, |doc| { @@ -371,6 +384,7 @@ pub fn get_trait_def(cdata: Cmd, let tp_defs = item_ty_param_defs(item_doc, tcx, cdata, tag_items_data_item_ty_param_bounds); let rp_defs = item_region_param_defs(item_doc, cdata); + let sized = item_sized(item_doc); let mut bounds = ty::EmptyBuiltinBounds(); // Collect the builtin bounds from the encoded supertraits. // FIXME(#8559): They should be encoded directly. @@ -382,6 +396,13 @@ pub fn get_trait_def(cdata: Cmd, }); true }); + // Turn sized into a bound, FIXME(#8559). + if sized == ast::StaticSize { + tcx.lang_items.to_builtin_kind(tcx.lang_items.sized_trait().unwrap()).map(|bound| { + bounds.add(bound); + }); + } + ty::TraitDef { generics: ty::Generics {type_param_defs: tp_defs, region_param_defs: rp_defs}, diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 848fd8d362e..d5ee1b15ae2 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -832,6 +832,16 @@ fn encode_extension_implementations(ecx: &EncodeContext, } } +fn encode_sized(ebml_w: &mut Encoder, sized: Sized) { + ebml_w.start_tag(tag_items_data_item_sized); + let ch = match sized { + DynSize => 'd', + StaticSize => 's', + }; + ebml_w.wr_str(str::from_char(ch)); + ebml_w.end_tag(); +} + fn encode_info_for_item(ecx: &EncodeContext, ebml_w: &mut Encoder, item: &Item, @@ -1070,7 +1080,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ast_method) } } - ItemTrait(_, ref super_traits, ref ms) => { + ItemTrait(_, sized, ref super_traits, ref ms) => { add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); @@ -1084,6 +1094,9 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_trait_ref(ebml_w, ecx, &*trait_def.trait_ref, tag_item_trait_ref); encode_name(ebml_w, item.ident.name); encode_attributes(ebml_w, item.attrs.as_slice()); + // When we fix the rest of the supertrait nastiness (FIXME(#8559)), we + // should no longer need this ugly little hack either. + encode_sized(ebml_w, sized); encode_visibility(ebml_w, vis); for &method_def_id in ty::trait_method_def_ids(tcx, def_id).iter() { ebml_w.start_tag(tag_item_trait_method); diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 427a7f406b0..eeccd1ca334 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -79,7 +79,7 @@ impl Visitor<()> for ParentVisitor { // method to the root. In this case, if the trait is private, then // parent all the methods to the trait to indicate that they're // private. - ast::ItemTrait(_, _, ref methods) if item.vis != ast::Public => { + ast::ItemTrait(_, _, _, ref methods) if item.vis != ast::Public => { for m in methods.iter() { match *m { ast::Provided(ref m) => self.parents.insert(m.id, item.id), @@ -274,7 +274,7 @@ impl<'a> Visitor<()> for EmbargoVisitor<'a> { // Default methods on traits are all public so long as the trait // is public - ast::ItemTrait(_, _, ref methods) if public_first => { + ast::ItemTrait(_, _, _, ref methods) if public_first => { for method in methods.iter() { match *method { ast::Provided(ref m) => { @@ -1082,7 +1082,7 @@ impl<'a> SanePrivacyVisitor<'a> { } } - ast::ItemTrait(_, _, ref methods) => { + ast::ItemTrait(_, _, _, ref methods) => { for m in methods.iter() { match *m { ast::Provided(ref m) => { @@ -1142,7 +1142,7 @@ impl<'a> SanePrivacyVisitor<'a> { ast::ItemStruct(ref def, _) => check_struct(def), - ast::ItemTrait(_, _, ref methods) => { + ast::ItemTrait(_, _, _, ref methods) => { for m in methods.iter() { match *m { ast::Required(..) => {} diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index d645b628aff..a2e6d6d1cab 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -1325,7 +1325,7 @@ impl<'a> Resolver<'a> { ItemImpl(_, Some(_), _, _) => parent, - ItemTrait(_, _, ref methods) => { + ItemTrait(_, _, _, ref methods) => { let name_bindings = self.add_child(ident, parent.clone(), ForbidDuplicateTypes, sp); @@ -3578,7 +3578,7 @@ impl<'a> Resolver<'a> { methods.as_slice()); } - ItemTrait(ref generics, ref traits, ref methods) => { + ItemTrait(ref generics, _, ref traits, ref methods) => { // Create a new rib for the self type. let self_type_rib = Rib::new(NormalRibKind); // plain insert (no renaming) @@ -3786,9 +3786,8 @@ impl<'a> Resolver<'a> { } Some(declaration) => { for argument in declaration.inputs.iter() { - let binding_mode = ArgumentIrrefutableMode; this.resolve_pattern(argument.pat, - binding_mode, + ArgumentIrrefutableMode, None); this.resolve_type(argument.ty); diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 130c93ac51e..88989f7ce08 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -84,7 +84,7 @@ impl<'a, 'b> Visitor> for LifetimeContext<'b> { ast::ItemEnum(_, ref generics) | ast::ItemStruct(_, ref generics) | ast::ItemImpl(ref generics, _, _, _) | - ast::ItemTrait(ref generics, _, _) => { + ast::ItemTrait(ref generics, _, _, _) => { self.check_lifetime_names(&generics.lifetimes); EarlyScope(0, &generics.lifetimes, &root) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 9a3bdcc1e15..49968de28da 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2591,16 +2591,24 @@ pub fn type_is_machine(ty: t) -> bool { #[allow(dead_code)] // leaving in for DST pub fn type_is_sized(cx: &ctxt, ty: ty::t) -> bool { match get(ty).sty { - // FIXME(#6308) add trait, vec, str, etc here. - ty_param(p) => { + ty_param(tp) => { + assert_eq!(tp.def_id.krate, ast::LOCAL_CRATE); + let ty_param_defs = cx.ty_param_defs.borrow(); - let param_def = ty_param_defs.get(&p.def_id.node); - if param_def.bounds.builtin_bounds.contains_elem(BoundSized) { - return true; - } - return false; + let param_def = ty_param_defs.get(&tp.def_id.node); + param_def.bounds.builtin_bounds.contains_elem(BoundSized) }, - _ => return true, + ty_self(def_id) => { + let trait_def = lookup_trait_def(cx, def_id); + trait_def.bounds.contains_elem(BoundSized) + }, + ty_struct(def_id, ref substs) => { + let flds = lookup_struct_fields(cx, def_id); + let mut tps = flds.iter().map(|f| lookup_field_type(cx, def_id, f.id, substs)); + !tps.any(|ty| !type_is_sized(cx, ty)) + } + ty_tup(ref ts) => !ts.iter().any(|t| !type_is_sized(cx, *t)), + _ => true } } @@ -3495,7 +3503,7 @@ pub fn provided_trait_methods(cx: &ctxt, id: ast::DefId) -> Vec> { match cx.map.find(id.node) { Some(ast_map::NodeItem(item)) => { match item.node { - ItemTrait(_, _, ref ms) => { + ItemTrait(_, _, _, ref ms) => { let (_, p) = ast_util::split_trait_methods(ms.as_slice()); p.iter().map(|m| method(cx, ast_util::local_def(m.id))).collect() } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index dc5b4f6d520..cd880f80817 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -369,21 +369,21 @@ impl<'a> GatherLocalsVisitor<'a> { } impl<'a> Visitor<()> for GatherLocalsVisitor<'a> { - // Add explicitly-declared locals. + // Add explicitly-declared locals. fn visit_local(&mut self, local: &ast::Local, _: ()) { - let o_ty = match local.ty.node { - ast::TyInfer => None, - _ => Some(self.fcx.to_ty(local.ty)) - }; - self.assign(local.id, o_ty); - debug!("Local variable {} is assigned type {}", - self.fcx.pat_to_str(local.pat), - self.fcx.infcx().ty_to_str( - self.fcx.inh.locals.borrow().get_copy(&local.id))); - visit::walk_local(self, local, ()); - + let o_ty = match local.ty.node { + ast::TyInfer => None, + _ => Some(self.fcx.to_ty(local.ty)) + }; + self.assign(local.id, o_ty); + debug!("Local variable {} is assigned type {}", + self.fcx.pat_to_str(local.pat), + self.fcx.infcx().ty_to_str( + self.fcx.inh.locals.borrow().get_copy(&local.id))); + visit::walk_local(self, local, ()); } - // Add pattern bindings. + + // Add pattern bindings. fn visit_pat(&mut self, p: &ast::Pat, _: ()) { match p.node { ast::PatIdent(_, ref path, _) @@ -561,6 +561,26 @@ 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() { + let t = ty::node_id_to_type(tcx, f.node.id); + if !ty::type_is_sized(tcx, t) { + match f.node.kind { + ast::NamedField(ident, _) => { + tcx.sess.span_err(f.span, format!("Dynamically sized type in field {}", + token::get_ident(ident))); + } + ast::UnnamedField(_) => { + tcx.sess.span_err(f.span, "Dynamically sized type in field"); + } + } + } + } +} + pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) { let tcx = ccx.tcx; @@ -568,7 +588,10 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) { check_representable(tcx, span, id, "struct"); // Check that the struct is instantiable - check_instantiable(tcx, span, id); + if check_instantiable(tcx, span, id) { + // This might cause stack overflow if id is not instantiable. + check_fields_sized(tcx, id); + } // Check there are no overlapping fields in super-structs check_for_field_shadowing(tcx, local_def(id)); @@ -630,7 +653,7 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) { } } - ast::ItemTrait(_, _, ref trait_methods) => { + ast::ItemTrait(_, _, _, ref trait_methods) => { let trait_def = ty::lookup_trait_def(ccx.tcx, local_def(it.id)); for trait_method in (*trait_methods).iter() { match *trait_method { @@ -3468,14 +3491,16 @@ pub fn check_representable(tcx: &ty::ctxt, /// is representable, but not instantiable. pub fn check_instantiable(tcx: &ty::ctxt, sp: Span, - item_id: ast::NodeId) { + item_id: ast::NodeId) -> bool { let item_ty = ty::node_id_to_type(tcx, item_id); if !ty::is_instantiable(tcx, item_ty) { tcx.sess.span_err(sp, format!("this type cannot be instantiated \ without an instance of itself; \ consider using `Option<{}>`", ppaux::ty_to_str(tcx, item_ty))); + return false } + true } pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) { diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 0102001aa3c..0e1419dafa0 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -32,6 +32,7 @@ are represented as `ty_param()` instances. use metadata::csearch; +use middle::lang_items::SizedTraitLangItem; use middle::resolve_lifetime; use middle::ty::{ImplContainer, MethodContainer, TraitContainer, substs}; use middle::ty::{ty_param_bounds_and_ty}; @@ -189,7 +190,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) { match tcx.map.get(trait_id) { ast_map::NodeItem(item) => { match item.node { - ast::ItemTrait(ref generics, _, ref ms) => { + ast::ItemTrait(ref generics, _, _, ref ms) => { let trait_ty_generics = ty_generics_for_type(ccx, generics); // For each method, construct a suitable ty::Method and @@ -402,7 +403,8 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt, trait_id: ast::NodeId) { pub fn ensure_supertraits(ccx: &CrateCtxt, id: ast::NodeId, sp: codemap::Span, - ast_trait_refs: &[ast::TraitRef]) + ast_trait_refs: &[ast::TraitRef], + sized: ast::Sized) -> ty::BuiltinBounds { let tcx = ccx.tcx; @@ -433,6 +435,12 @@ pub fn ensure_supertraits(ccx: &CrateCtxt, } } } + if sized == ast::StaticSize { + match tcx.lang_items.require(SizedTraitLangItem) { + Ok(def_id) => { ty::try_add_builtin_trait(tcx, def_id, &mut bounds); }, + Err(s) => tcx.sess.err(s), + }; + } tcx.supertraits.borrow_mut().insert(local_def(id), Rc::new(ty_trait_refs)); bounds @@ -562,8 +570,7 @@ pub fn ensure_no_ty_param_bounds(ccx: &CrateCtxt, if ty_param.bounds.len() > 0 { ccx.tcx.sess.span_err( span, - format!("trait bounds are not allowed in {} definitions", - thing)); + format!("trait bounds are not allowed in {} definitions", thing)); } } } @@ -634,7 +641,7 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { } } }, - ast::ItemTrait(ref generics, _, ref trait_methods) => { + ast::ItemTrait(ref generics, _, _, ref trait_methods) => { let trait_def = trait_def_of_item(ccx, it); // Run convert_methods on the provided methods. @@ -863,14 +870,15 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc { } match it.node { - ast::ItemTrait(ref generics, ref supertraits, _) => { + ast::ItemTrait(ref generics, sized, ref supertraits, _) => { let self_ty = ty::mk_self(tcx, def_id); let ty_generics = ty_generics_for_type(ccx, generics); let substs = mk_item_substs(ccx, &ty_generics, Some(self_ty)); let bounds = ensure_supertraits(ccx, it.id, it.span, - supertraits.as_slice()); + supertraits.as_slice(), + sized); let trait_def = Rc::new(ty::TraitDef { generics: ty_generics, bounds: bounds, @@ -1032,7 +1040,12 @@ fn ty_generics(ccx: &CrateCtxt, existing_def_opt.unwrap_or_else(|| { let param_ty = ty::param_ty {idx: base_index + offset, def_id: local_def(param.id)}; - let bounds = Rc::new(compute_bounds(ccx, param_ty, ¶m.bounds)); + let bounds = Rc::new(compute_bounds(ccx, + param_ty, + ¶m.bounds, + param.sized, + param.ident, + param.span)); let default = param.default.map(|path| { let ty = ast_ty_to_ty(ccx, &ExplicitRscope, path); let cur_idx = param_ty.idx; @@ -1067,7 +1080,10 @@ fn ty_generics(ccx: &CrateCtxt, fn compute_bounds( ccx: &CrateCtxt, param_ty: ty::param_ty, - ast_bounds: &OwnedSlice) -> ty::ParamBounds + ast_bounds: &OwnedSlice, + sized: ast::Sized, + ident: ast::Ident, + span: Span) -> ty::ParamBounds { /*! * Translate the AST's notion of ty param bounds (which are an @@ -1086,9 +1102,8 @@ fn ty_generics(ccx: &CrateCtxt, let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id); let trait_ref = instantiate_trait_ref(ccx, b, ty); if !ty::try_add_builtin_trait( - ccx.tcx, trait_ref.def_id, - &mut param_bounds.builtin_bounds) - { + ccx.tcx, trait_ref.def_id, + &mut param_bounds.builtin_bounds) { // Must be a user-defined trait param_bounds.trait_bounds.push(trait_ref); } @@ -1100,8 +1115,45 @@ fn ty_generics(ccx: &CrateCtxt, } } + if sized == ast::StaticSize { + match ccx.tcx.lang_items.require(SizedTraitLangItem) { + Ok(def_id) => { ty::try_add_builtin_trait(ccx.tcx, + def_id, + &mut param_bounds.builtin_bounds); }, + // Fixme(13367) after `type` makes it into the snapshot, we can check this properly + Err(_s) => {}, //ccx.tcx.sess.err(s), + } + } + + check_bounds_compatible(ccx.tcx, ¶m_bounds, ident, span); + param_bounds } + + fn check_bounds_compatible(tcx: &ty::ctxt, + param_bounds: &ty::ParamBounds, + ident: ast::Ident, + span: Span) { + // Currently the only bound which is incompatible with other bounds is + // Sized/Unsized. + if !param_bounds.builtin_bounds.contains_elem(ty::BoundSized) { + ty::each_bound_trait_and_supertraits(tcx, + param_bounds.trait_bounds.as_slice(), + |trait_ref| { + let trait_def = ty::lookup_trait_def(tcx, trait_ref.def_id); + for bound in trait_def.bounds.iter() { + if bound == ty::BoundSized { + tcx.sess.span_err(span, + format!("incompatible bounds on type parameter {}, \ + bound {} does not allow unsized type", + token::get_ident(ident), + ppaux::trait_ref_to_str(tcx, trait_ref))); + } + } + true + }); + } + } } pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index a5d41b15d5d..bb9c6a87bab 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -878,6 +878,8 @@ impl<'a> Rebuilder<'a> { id: ty_param.id, bounds: bounds, default: ty_param.default, + span: ty_param.span, + sized: ty_param.sized, } }) } diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs index 9e7c221d3c5..5485cddb476 100644 --- a/src/librustc/middle/typeck/variance.rs +++ b/src/librustc/middle/typeck/variance.rs @@ -347,7 +347,7 @@ impl<'a> Visitor<()> for TermsContext<'a> { match item.node { ast::ItemEnum(_, ref generics) | ast::ItemStruct(_, ref generics) | - ast::ItemTrait(ref generics, _, _) => { + ast::ItemTrait(ref generics, _, _, _) => { for (i, p) in generics.lifetimes.iter().enumerate() { self.add_inferred(item.id, RegionParam, i, p.id); } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 7de898a50a9..3fc65dd9647 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -261,7 +261,7 @@ impl<'a> RustdocVisitor<'a> { }; om.statics.push(s); }, - ast::ItemTrait(ref gen, ref tr, ref met) => { + ast::ItemTrait(ref gen, _, ref tr, ref met) => { let t = Trait { name: item.ident, methods: met.iter().map(|x| (*x).clone()).collect(), diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 5d0b24fdb3e..97ddff78789 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -180,6 +180,7 @@ pub enum TyParamBound { pub struct TyParam { pub ident: Ident, pub id: NodeId, + pub sized: Sized, pub bounds: OwnedSlice, pub default: Option>, pub span: Span @@ -1051,6 +1052,12 @@ impl Visibility { } } +#[deriving(Clone, Eq, TotalEq, Encodable, Decodable, Hash)] +pub enum Sized { + DynSize, + StaticSize, +} + #[deriving(Clone, Eq, TotalEq, Encodable, Decodable, Hash)] pub struct StructField_ { pub kind: StructFieldKind, @@ -1109,7 +1116,7 @@ pub enum Item_ { ItemTy(P, Generics), ItemEnum(EnumDef, Generics), ItemStruct(@StructDef, Generics), - ItemTrait(Generics, Vec , Vec ), + ItemTrait(Generics, Sized, Vec , Vec ), ItemImpl(Generics, Option, // (optional) trait this impl implements P, // self diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index 45954800e7e..d0e3ff4ae54 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -265,6 +265,24 @@ impl Map { } } + pub fn expect_struct(&self, id: NodeId) -> @StructDef { + match self.find(id) { + Some(NodeItem(i)) => { + match i.node { + ItemStruct(struct_def, _) => struct_def, + _ => fail!("struct ID bound to non-struct") + } + } + Some(NodeVariant(ref variant)) => { + match (*variant).node.kind { + StructVariantKind(struct_def) => struct_def, + _ => fail!("struct ID bound to enum variant that isn't struct-like"), + } + } + _ => fail!(format!("expected struct, found {}", self.node_to_str(id))), + } + } + pub fn expect_foreign_item(&self, id: NodeId) -> @ForeignItem { match self.find(id) { Some(NodeForeignItem(item)) => item, @@ -453,7 +471,7 @@ impl<'a, F: FoldOps> Folder for Ctx<'a, F> { None => {} } } - ItemTrait(_, ref traits, ref methods) => { + ItemTrait(_, _, ref traits, ref methods) => { for t in traits.iter() { self.insert(t.ref_id, EntryItem(self.parent, i)); } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index c1289ef9858..e1174ea6cc4 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -68,6 +68,7 @@ pub trait AstBuilder { fn typaram(&self, span: Span, id: ast::Ident, + sized: ast::Sized, bounds: OwnedSlice, default: Option>) -> ast::TyParam; @@ -371,11 +372,13 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn typaram(&self, span: Span, id: ast::Ident, + sized: ast::Sized, bounds: OwnedSlice, default: Option>) -> ast::TyParam { ast::TyParam { ident: id, id: ast::DUMMY_NODE_ID, + sized: sized, bounds: bounds, default: default, span: span diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs index 579de82c8db..35a1eb0bb83 100644 --- a/src/libsyntax/ext/deriving/decodable.rs +++ b/src/libsyntax/ext/deriving/decodable.rs @@ -13,6 +13,7 @@ The compiler code necessary for `#[deriving(Decodable)]`. See encodable.rs for more. */ +use ast; use ast::{MetaItem, Item, Expr, MutMutable, Ident}; use codemap::Span; use ext::base::ExtCtxt; @@ -35,10 +36,10 @@ pub fn expand_deriving_decodable(cx: &mut ExtCtxt, additional_bounds: Vec::new(), generics: LifetimeBounds { lifetimes: Vec::new(), - bounds: vec!(("__D", vec!(Path::new_( + bounds: vec!(("__D", ast::StaticSize, vec!(Path::new_( vec!("serialize", "Decoder"), None, vec!(~Literal(Path::new_local("__E"))), true))), - ("__E", vec!())) + ("__E", ast::StaticSize, vec!())) }, methods: vec!( MethodDef { diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs index 1c3edce96fb..806560f6826 100644 --- a/src/libsyntax/ext/deriving/encodable.rs +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -82,6 +82,7 @@ would yield functions like: ``` */ +use ast; use ast::{MetaItem, Item, Expr, ExprRet, MutMutable, LitNil}; use codemap::Span; use ext::base::ExtCtxt; @@ -103,10 +104,10 @@ pub fn expand_deriving_encodable(cx: &mut ExtCtxt, additional_bounds: Vec::new(), generics: LifetimeBounds { lifetimes: Vec::new(), - bounds: vec!(("__S", vec!(Path::new_( + bounds: vec!(("__S", ast::StaticSize, vec!(Path::new_( vec!("serialize", "Encoder"), None, vec!(~Literal(Path::new_local("__E"))), true))), - ("__E", vec!())) + ("__E", ast::StaticSize, vec!())) }, methods: vec!( MethodDef { diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index c040361a8eb..914451fb402 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -380,7 +380,11 @@ impl<'a> TraitDef<'a> { // require the current trait bounds.push(cx.typarambound(trait_path.clone())); - cx.typaram(self.span, ty_param.ident, OwnedSlice::from_vec(bounds), None) + cx.typaram(self.span, + ty_param.ident, + ty_param.sized, + OwnedSlice::from_vec(bounds), + None) })); let trait_generics = Generics { lifetimes: lifetimes, diff --git a/src/libsyntax/ext/deriving/hash.rs b/src/libsyntax/ext/deriving/hash.rs index 23331dc03cb..d22027d203f 100644 --- a/src/libsyntax/ext/deriving/hash.rs +++ b/src/libsyntax/ext/deriving/hash.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use ast; use ast::{MetaItem, Item, Expr, MutMutable}; use codemap::Span; use ext::base::ExtCtxt; @@ -25,7 +26,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt, vec!(~Literal(Path::new_local("__S"))), true), LifetimeBounds { lifetimes: Vec::new(), - bounds: vec!(("__S", vec!(Path::new(vec!("std", "io", "Writer"))))), + bounds: vec!(("__S", ast::StaticSize, vec!(Path::new(vec!("std", "io", "Writer"))))), }, Path::new_local("__S")) } else { diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index 6b824e52bb3..e81aa55d10d 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -32,7 +32,8 @@ pub fn expand_deriving_rand(cx: &mut ExtCtxt, generics: LifetimeBounds { lifetimes: Vec::new(), bounds: vec!(("R", - vec!( Path::new(vec!("rand", "Rng")) ))) + ast::StaticSize, + vec!( Path::new(vec!("rand", "Rng")) ))) }, explicit_self: None, args: vec!( diff --git a/src/libsyntax/ext/deriving/ty.rs b/src/libsyntax/ext/deriving/ty.rs index a6bbad62b8e..6e3327b4039 100644 --- a/src/libsyntax/ext/deriving/ty.rs +++ b/src/libsyntax/ext/deriving/ty.rs @@ -186,14 +186,14 @@ impl<'a> Ty<'a> { } -fn mk_ty_param(cx: &ExtCtxt, span: Span, name: &str, bounds: &[Path], +fn mk_ty_param(cx: &ExtCtxt, span: Span, name: &str, sized: ast::Sized, bounds: &[Path], self_ident: Ident, self_generics: &Generics) -> ast::TyParam { let bounds = bounds.iter().map(|b| { let path = b.to_path(cx, span, self_ident, self_generics); cx.typarambound(path) }).collect(); - cx.typaram(span, cx.ident_of(name), bounds, None) + cx.typaram(span, cx.ident_of(name), sized, bounds, None) } fn mk_generics(lifetimes: Vec , ty_params: Vec ) -> Generics { @@ -206,7 +206,7 @@ fn mk_generics(lifetimes: Vec , ty_params: Vec ) - /// Lifetimes and bounds on type parameters pub struct LifetimeBounds<'a> { pub lifetimes: Vec<&'a str>, - pub bounds: Vec<(&'a str, Vec>)>, + pub bounds: Vec<(&'a str, ast::Sized, Vec>)>, } impl<'a> LifetimeBounds<'a> { @@ -226,10 +226,11 @@ impl<'a> LifetimeBounds<'a> { }).collect(); let ty_params = self.bounds.iter().map(|t| { match t { - &(ref name, ref bounds) => { + &(ref name, sized, ref bounds) => { mk_ty_param(cx, span, *name, + sized, bounds.as_slice(), self_ty, self_generics) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 72be633d456..04b289b9fca 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -447,6 +447,7 @@ pub fn fold_ty_param(tp: &TyParam, fld: &mut T) -> TyParam { TyParam { ident: tp.ident, id: id, + sized: tp.sized, bounds: tp.bounds.map(|x| fold_ty_param_bound(x, fld)), default: tp.default.map(|x| fld.fold_ty(x)), span: tp.span @@ -620,7 +621,7 @@ pub fn noop_fold_item_underscore(i: &Item_, folder: &mut T) -> Item_ methods.iter().map(|x| folder.fold_method(*x)).collect() ) } - ItemTrait(ref generics, ref traits, ref methods) => { + ItemTrait(ref generics, ref sized, ref traits, ref methods) => { let methods = methods.iter().map(|method| { match *method { Required(ref m) => Required(folder.fold_type_method(m)), @@ -628,6 +629,7 @@ pub fn noop_fold_item_underscore(i: &Item_, folder: &mut T) -> Item_ } }).collect(); ItemTrait(fold_generics(generics, folder), + *sized, traits.iter().map(|p| fold_trait_ref(p, folder)).collect(), methods) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 85480bebc90..8808312bed7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -42,6 +42,7 @@ use ast::{PatIdent, PatLit, PatRange, PatRegion, PatStruct}; use ast::{PatTup, PatUniq, PatWild, PatWildMulti}; use ast::{BiRem, Required}; use ast::{RetStyle, Return, BiShl, BiShr, Stmt, StmtDecl}; +use ast::{Sized, DynSize, StaticSize}; use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField}; use ast::{StructVariantKind, BiSub}; use ast::StrStyle; @@ -3390,10 +3391,11 @@ impl<'a> Parser<'a> { return (ret_lifetime, Some(OwnedSlice::from_vec(result))); } - // matches typaram = IDENT optbounds ( EQ ty )? + // matches typaram = type? IDENT optbounds ( EQ ty )? fn parse_ty_param(&mut self) -> TyParam { - let ident = self.parse_ident(); + let sized = self.parse_sized(); let span = self.span; + let ident = self.parse_ident(); let (_, opt_bounds) = self.parse_optional_ty_param_bounds(false); // For typarams we don't care about the difference b/w "" and "". let bounds = opt_bounds.unwrap_or_default(); @@ -3407,6 +3409,7 @@ impl<'a> Parser<'a> { TyParam { ident: ident, id: ast::DUMMY_NODE_ID, + sized: sized, bounds: bounds, default: default, span: span, @@ -3797,6 +3800,7 @@ impl<'a> Parser<'a> { fn parse_item_trait(&mut self) -> ItemInfo { let ident = self.parse_ident(); let tps = self.parse_generics(); + let sized = self.parse_for_sized(); // Parse traits, if necessary. let traits; @@ -3808,7 +3812,7 @@ impl<'a> Parser<'a> { } let meths = self.parse_trait_methods(); - (ident, ItemTrait(tps, traits, meths), None) + (ident, ItemTrait(tps, sized, traits, meths), None) } // Parses two variants (with the region/type params always optional): @@ -3999,6 +4003,23 @@ impl<'a> Parser<'a> { else { Inherited } } + fn parse_sized(&mut self) -> Sized { + if self.eat_keyword(keywords::Type) { DynSize } + else { StaticSize } + } + + fn parse_for_sized(&mut self) -> Sized { + if self.eat_keyword(keywords::For) { + if !self.eat_keyword(keywords::Type) { + self.span_err(self.last_span, + "expected 'type' after for in trait item"); + } + DynSize + } else { + StaticSize + } + } + // given a termination token and a vector of already-parsed // attributes (of length 0 or 1), parse all of the items in a module fn parse_mod_items(&mut self, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index f4e337e2048..57438169d6d 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -673,10 +673,13 @@ impl<'a> State<'a> { } try!(self.bclose(item.span)); } - ast::ItemTrait(ref generics, ref traits, ref methods) => { + ast::ItemTrait(ref generics, ref sized, ref traits, ref methods) => { try!(self.head(visibility_qualified(item.vis, "trait"))); try!(self.print_ident(item.ident)); try!(self.print_generics(generics)); + if *sized == ast::DynSize { + try!(self.word_space("for type")); + } if traits.len() != 0u { try!(word(&mut self.s, ":")); for (i, trait_) in traits.iter().enumerate() { @@ -1910,6 +1913,9 @@ impl<'a> State<'a> { } else { let idx = idx - generics.lifetimes.len(); let param = generics.ty_params.get(idx); + if param.sized == ast::DynSize { + try!(s.word_space("type")); + } try!(s.print_ident(param.ident)); try!(s.print_bounds(&None, ¶m.bounds, false)); match param.default { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 1f75c2e062f..e830daf8ede 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -257,7 +257,7 @@ pub fn walk_item>(visitor: &mut V, item: &Item, env: E) item.id, env) } - ItemTrait(ref generics, ref trait_paths, ref methods) => { + ItemTrait(ref generics, _, ref trait_paths, ref methods) => { visitor.visit_generics(generics, env.clone()); for trait_path in trait_paths.iter() { visitor.visit_path(&trait_path.path, diff --git a/src/test/compile-fail/bad-mid-path-type-params.rs b/src/test/compile-fail/bad-mid-path-type-params.rs index 1b72e468428..58ed6624cbf 100644 --- a/src/test/compile-fail/bad-mid-path-type-params.rs +++ b/src/test/compile-fail/bad-mid-path-type-params.rs @@ -12,6 +12,9 @@ #![no_std] +#[lang="sized"] +pub trait Sized {} + struct S { contents: T, } diff --git a/src/test/compile-fail/borrowck-move-subcomponent.rs b/src/test/compile-fail/borrowck-move-subcomponent.rs index f6ca6ac42b2..6940c85e0e6 100644 --- a/src/test/compile-fail/borrowck-move-subcomponent.rs +++ b/src/test/compile-fail/borrowck-move-subcomponent.rs @@ -13,6 +13,9 @@ #![no_std] +#[lang="sized"] +pub trait Sized {} + struct S { x : ~int } diff --git a/src/test/compile-fail/lint-dead-code-1.rs b/src/test/compile-fail/lint-dead-code-1.rs index 04326403376..bbcc59f0d6c 100644 --- a/src/test/compile-fail/lint-dead-code-1.rs +++ b/src/test/compile-fail/lint-dead-code-1.rs @@ -17,6 +17,10 @@ #![crate_type="lib"] pub use foo2::Bar2; + +#[lang="sized"] +pub trait Sized {} + mod foo { pub struct Bar; //~ ERROR: code is never used } diff --git a/src/test/compile-fail/privacy1.rs b/src/test/compile-fail/privacy1.rs index 45d13bd7996..83141020b29 100644 --- a/src/test/compile-fail/privacy1.rs +++ b/src/test/compile-fail/privacy1.rs @@ -11,6 +11,9 @@ #![feature(globs)] #![no_std] // makes debugging this test *a lot* easier (during resolve) +#[lang="sized"] +pub trait Sized {} + mod bar { // shouln't bring in too much pub use self::glob::*; diff --git a/src/test/compile-fail/unsized-bare-typaram.rs b/src/test/compile-fail/unsized-bare-typaram.rs index ff9d379c30f..fd09d78a4fa 100644 --- a/src/test/compile-fail/unsized-bare-typaram.rs +++ b/src/test/compile-fail/unsized-bare-typaram.rs @@ -10,5 +10,5 @@ // error-pattern: instantiating a type parameter with an incompatible type fn bar() { } -fn foo() { bar::() } +fn foo() { bar::() } fn main() { } diff --git a/src/test/compile-fail/unsized-enum.rs b/src/test/compile-fail/unsized-enum.rs index df7d82f0b25..f586fbb576b 100644 --- a/src/test/compile-fail/unsized-enum.rs +++ b/src/test/compile-fail/unsized-enum.rs @@ -10,5 +10,5 @@ // error-pattern: instantiating a type parameter with an incompatible type fn bar() { } -fn foo() { bar::>() } +fn foo() { bar::>() } fn main() { } diff --git a/src/test/compile-fail/unsized-struct.rs b/src/test/compile-fail/unsized-struct.rs index da3e9e12430..9fab3accbb9 100644 --- a/src/test/compile-fail/unsized-struct.rs +++ b/src/test/compile-fail/unsized-struct.rs @@ -13,5 +13,5 @@ struct Foo { data: T } fn bar() { } -fn foo() { bar::>() } +fn foo() { bar::>() } fn main() { } diff --git a/src/test/compile-fail/unsized.rs b/src/test/compile-fail/unsized.rs new file mode 100644 index 00000000000..d5c2bbb21ca --- /dev/null +++ b/src/test/compile-fail/unsized.rs @@ -0,0 +1,16 @@ +// 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. + +// Test syntax checks for `type` keyword. + +struct S1 for type; //~ ERROR expected `{`, `(`, or `;` after struct name but found `for` + +pub fn main() { +} diff --git a/src/test/compile-fail/unsized2.rs b/src/test/compile-fail/unsized2.rs new file mode 100644 index 00000000000..0c9d05e2988 --- /dev/null +++ b/src/test/compile-fail/unsized2.rs @@ -0,0 +1,17 @@ +// 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. + +// Test syntax checks for `type` keyword. + +fn f() {} + +pub fn main() { + f(); //~ ERROR found `type` in ident position +} diff --git a/src/test/compile-fail/unsized3.rs b/src/test/compile-fail/unsized3.rs new file mode 100644 index 00000000000..d842a289289 --- /dev/null +++ b/src/test/compile-fail/unsized3.rs @@ -0,0 +1,51 @@ +// 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. + +// Test sized-ness checking in substitution. + +// Unbounded. +fn f1(x: &X) { + f2::(x); //~ ERROR instantiating a type parameter with an incompatible type `X`, which does n +} +fn f2(x: &X) { +} + +// Bounded. +trait T for type {} +fn f3(x: &X) { + f4::(x); //~ ERROR instantiating a type parameter with an incompatible type `X`, which does n +} +fn f4(x: &X) { +} + +// I would like these to fail eventually. +/* +// impl - bounded +trait T1 { +} +struct S3; +impl T1 for S3 { //ERROR instantiating a type parameter with an incompatible type +} + +// impl - unbounded +trait T2 { +} +impl T2 for S3 { //ERROR instantiating a type parameter with an incompatible type `X` + +// impl - struct +trait T3 { +} +struct S4; +impl T3 for S4 { //ERROR instantiating a type parameter with an incompatible type `X` +} +*/ + +pub fn main() { +} diff --git a/src/test/compile-fail/unsized4.rs b/src/test/compile-fail/unsized4.rs new file mode 100644 index 00000000000..968716320fd --- /dev/null +++ b/src/test/compile-fail/unsized4.rs @@ -0,0 +1,19 @@ +// 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. + +// Test that bounds are sized-compatible. + +trait T {} +fn f() { +//~^ERROR incompatible bounds on type parameter Y, bound T does not allow unsized type +} + +pub fn main() { +} diff --git a/src/test/run-pass/unsized.rs b/src/test/run-pass/unsized.rs new file mode 100644 index 00000000000..e7dadd4b5eb --- /dev/null +++ b/src/test/run-pass/unsized.rs @@ -0,0 +1,26 @@ +// 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. + +// Test syntax checks for `type` keyword. + +trait T1 for type {} +pub trait T2 for type {} +trait T3 for type: T2 {} +trait T4 {} +trait T5 {} +trait T6 {} +trait T7 {} +trait T8 {} +struct S1; +impl T1 for S1 {} +fn f() {} + +pub fn main() { +} diff --git a/src/test/run-pass/unsized2.rs b/src/test/run-pass/unsized2.rs new file mode 100644 index 00000000000..c8e8f98145f --- /dev/null +++ b/src/test/run-pass/unsized2.rs @@ -0,0 +1,82 @@ +// 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. + +// Test sized-ness checking in substitution. + +// Unbounded. +fn f1(x: &X) { + f1::(x); +} +fn f2(x: &X) { + f1::(x); + f2::(x); +} + +// Bounded. +trait T for type {} +fn f3(x: &X) { + f3::(x); +} +fn f4(x: &X) { + f3::(x); + f4::(x); +} + +// Self type. +trait T2 for type { + fn f() -> ~Self; +} +struct S; +impl T2 for S { + fn f() -> ~S { + ~S + } +} +fn f5(x: &X) { + let _: ~X = T2::f(); +} +fn f6(x: &X) { + let _: ~X = T2::f(); +} + +trait T3 for type { + fn f() -> ~Self; +} +impl T3 for S { + fn f() -> ~S { + ~S + } +} +fn f7(x: &X) { + // This is valid, but the unsized bound on X is irrelevant because any type + // which implements T3 must have statically known size. + let _: ~X = T3::f(); +} + +trait T4 { + fn m1(x: &T4); + fn m2(x: &T5); +} +trait T5 { + fn m1(x: &T4); // not an error (for now) + fn m2(x: &T5); +} + +trait T6 { + fn m1(x: &T4); + fn m2(x: &T5); +} +trait T7 { + fn m1(x: &T4); // not an error (for now) + fn m2(x: &T5); +} + +pub fn main() { +} From 0540a59382769dcb1981f0542cd227796fc8ecf1 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 8 Apr 2014 21:00:20 +1200 Subject: [PATCH 3/4] Check for unsized types in enums. And allow the last field of a struct or variant to be unsized. --- src/librustc/middle/ty.rs | 4 ++ src/librustc/middle/typeck/check/mod.rs | 82 ++++++++++++++++++++----- src/libsyntax/ast_map.rs | 7 +++ src/test/compile-fail/unsized3.rs | 29 +++++++++ src/test/compile-fail/unsized5.rs | 41 +++++++++++++ src/test/run-pass/unsized.rs | 1 + src/test/run-pass/unsized2.rs | 16 +++++ 7 files changed, 164 insertions(+), 16 deletions(-) create mode 100644 src/test/compile-fail/unsized5.rs 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() { } From 5729d9b41392ad153224acc2160f62205c9c1374 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Sun, 20 Apr 2014 18:53:37 +1200 Subject: [PATCH 4/4] Review changes --- src/librustc/middle/ty.rs | 25 +--------------- src/librustc/middle/typeck/check/mod.rs | 39 +++++++++++++++++-------- src/librustc/middle/typeck/collect.rs | 14 ++++----- src/libsyntax/print/pprust.rs | 3 +- src/test/compile-fail/unsized5.rs | 8 ++--- src/test/run-pass/unsized2.rs | 6 ++-- 6 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 7e0e4abef9d..1c5f0948245 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2590,30 +2590,7 @@ pub fn type_is_machine(ty: t) -> bool { // Is the type's representation size known at compile time? #[allow(dead_code)] // leaving in for DST pub fn type_is_sized(cx: &ctxt, ty: ty::t) -> bool { - match get(ty).sty { - ty_param(tp) => { - assert_eq!(tp.def_id.krate, ast::LOCAL_CRATE); - - let ty_param_defs = cx.ty_param_defs.borrow(); - let param_def = ty_param_defs.get(&tp.def_id.node); - param_def.bounds.builtin_bounds.contains_elem(BoundSized) - }, - ty_self(def_id) => { - let trait_def = lookup_trait_def(cx, def_id); - trait_def.bounds.contains_elem(BoundSized) - }, - ty_struct(def_id, ref substs) => { - let flds = lookup_struct_fields(cx, def_id); - let mut tps = flds.iter().map(|f| lookup_field_type(cx, def_id, f.id, substs)); - !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 - } + type_contents(cx, ty).is_sized(cx) } // Whether a type is enum like, that is an enum type with only nullary diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index b4f22f3fa6e..5e05be1ac91 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -576,15 +576,20 @@ fn check_for_field_shadowing(tcx: &ty::ctxt, } fn check_fields_sized(tcx: &ty::ctxt, - struct_def: @ast::StructDef) { + struct_def: &ast::StructDef) { let len = struct_def.fields.len(); - for i in range(0, len) { - let f = struct_def.fields.get(i); + if len == 0 { + return; + } + for f in struct_def.fields.slice_to(len - 1).iter() { let t = ty::node_id_to_type(tcx, f.node.id); - if !ty::type_is_sized(tcx, t) && i < (len - 1) { + if !ty::type_is_sized(tcx, t) { match f.node.kind { ast::NamedField(ident, _) => { - tcx.sess.span_err(f.span, format!("type of field {} is dynamically sized", + tcx.sess.span_err(f.span, format!("type `{}` is dynamically sized. \ + dynamically sized types may only \ + appear as the type of the final \ + field in a struct", token::get_ident(ident))); } ast::UnnamedField(_) => { @@ -3519,16 +3524,18 @@ pub fn check_representable(tcx: &ty::ctxt, /// is representable, but not instantiable. pub fn check_instantiable(tcx: &ty::ctxt, sp: Span, - item_id: ast::NodeId) -> bool { + item_id: ast::NodeId) + -> bool { let item_ty = ty::node_id_to_type(tcx, item_id); if !ty::is_instantiable(tcx, item_ty) { tcx.sess.span_err(sp, format!("this type cannot be instantiated \ without an instance of itself; \ consider using `Option<{}>`", ppaux::ty_to_str(tcx, item_ty))); - return false + false + } else { + true } - true } pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) { @@ -3567,12 +3574,20 @@ pub fn check_enum_variants_sized(ccx: &CrateCtxt, 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); + let len = arg_tys.len(); + if len == 0 { + return; + } + for (i, t) in arg_tys.slice_to(len - 1).iter().enumerate() { // Allow the last field in an enum to be unsized. - if !ty::type_is_sized(ccx.tcx, *t) && i < args.len() -1 { + // We want to do this so that we can support smart pointers. + // A struct value with an unsized final field is itself + // unsized and we must track this in the type system. + if !ty::type_is_sized(ccx.tcx, *t) { ccx.tcx.sess.span_err(args.get(i).ty.span, - format!("type {} is dynamically sized", + format!("type `{}` is dynamically sized. \ + dynamically sized types may only \ + appear as the final type in a variant", ppaux::ty_to_str(ccx.tcx, *t))); } } diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 0e1419dafa0..9c49512d4dc 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -1141,14 +1141,12 @@ fn ty_generics(ccx: &CrateCtxt, param_bounds.trait_bounds.as_slice(), |trait_ref| { let trait_def = ty::lookup_trait_def(tcx, trait_ref.def_id); - for bound in trait_def.bounds.iter() { - if bound == ty::BoundSized { - tcx.sess.span_err(span, - format!("incompatible bounds on type parameter {}, \ - bound {} does not allow unsized type", - token::get_ident(ident), - ppaux::trait_ref_to_str(tcx, trait_ref))); - } + if trait_def.bounds.contains_elem(ty::BoundSized) { + tcx.sess.span_err(span, + format!("incompatible bounds on type parameter {}, \ + bound {} does not allow unsized type", + token::get_ident(ident), + ppaux::trait_ref_to_str(tcx, &*trait_ref))); } true }); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 57438169d6d..f768bf22ce0 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -678,7 +678,8 @@ impl<'a> State<'a> { try!(self.print_ident(item.ident)); try!(self.print_generics(generics)); if *sized == ast::DynSize { - try!(self.word_space("for type")); + try!(space(&mut self.s)); + try!(word(&mut self.s, "for type")); } if traits.len() != 0u { try!(word(&mut self.s, ":")); diff --git a/src/test/compile-fail/unsized5.rs b/src/test/compile-fail/unsized5.rs index c3b50dcdcaf..9608b7e6b20 100644 --- a/src/test/compile-fail/unsized5.rs +++ b/src/test/compile-fail/unsized5.rs @@ -23,18 +23,18 @@ fn f6(x: &X) { }*/ struct S1 { - f1: X, //~ ERROR type of field f1 is dynamically sized + f1: X, //~ ERROR type `f1` is dynamically sized. dynamically sized types may only appear as the f2: int, } struct S2 { f: int, - g: X, //~ ERROR type of field g is dynamically sized + g: X, //~ ERROR type `g` is dynamically sized. dynamically sized types may only appear as the ty 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 + 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 } pub fn main() { diff --git a/src/test/run-pass/unsized2.rs b/src/test/run-pass/unsized2.rs index 7cc2dfd816f..7883adad46a 100644 --- a/src/test/run-pass/unsized2.rs +++ b/src/test/run-pass/unsized2.rs @@ -66,7 +66,8 @@ trait T4 { fn m2(x: &T5); } trait T5 { - fn m1(x: &T4); // not an error (for now) + // not an error (for now) + fn m1(x: &T4); fn m2(x: &T5); } @@ -75,7 +76,8 @@ trait T6 { fn m2(x: &T5); } trait T7 { - fn m1(x: &T4); // not an error (for now) + // not an error (for now) + fn m1(x: &T4); fn m2(x: &T5); }