diff --git a/src/libextra/io_util.rs b/src/libextra/io_util.rs index 91424ae3ba2c0..11dea1c3a7081 100644 --- a/src/libextra/io_util.rs +++ b/src/libextra/io_util.rs @@ -10,6 +10,7 @@ use core::io::{Reader, BytesReader}; use core::io; +use core::cast; /// An implementation of the io::Reader interface which reads a buffer of bytes pub struct BufReader { @@ -29,10 +30,13 @@ impl BufReader { } fn as_bytes_reader(&self, f: &fn(&BytesReader) -> A) -> A { + // XXX FIXME(#5723) + let bytes = ::core::util::id::<&[u8]>(self.buf); + let bytes: &'static [u8] = unsafe { cast::transmute(bytes) }; // Recreating the BytesReader state every call since // I can't get the borrowing to work correctly let bytes_reader = BytesReader { - bytes: ::core::util::id::<&[u8]>(self.buf), + bytes: bytes, pos: @mut *self.pos }; diff --git a/src/libextra/test.rs b/src/libextra/test.rs index e058a098fa49e..ee0d3649467bf 100644 --- a/src/libextra/test.rs +++ b/src/libextra/test.rs @@ -423,7 +423,7 @@ type MonitorMsg = (TestDesc, TestResult); fn run_tests(opts: &TestOpts, tests: ~[TestDescAndFn], - callback: @fn(e: TestEvent)) { + callback: &fn(e: TestEvent)) { let filtered_tests = filter_tests(opts, tests); let filtered_descs = filtered_tests.map(|t| copy t.desc); diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 9c28da10e40d5..cb47c88d3d4fc 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -959,7 +959,7 @@ fn encode_info_for_item(ecx: &EncodeContext, encode_attributes(ebml_w, item.attrs); match ty.node { ast::ty_path(path, bounds, _) if path.idents.len() == 1 => { - assert!(bounds.is_empty()); + assert!(bounds.is_none()); encode_impl_type_basename(ecx, ebml_w, ast_util::path_to_ident(path)); } diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index b0b2a16cf8939..a1f595fc8969f 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -128,7 +128,7 @@ fn check_item(item: @item, (cx, visitor): (Context, visit::vt)) { // Yes, it's a destructor. match self_type.node { ty_path(_, bounds, path_node_id) => { - assert!(bounds.is_empty()); + assert!(bounds.is_none()); let struct_def = cx.tcx.def_map.get_copy( &path_node_id); let struct_did = @@ -169,10 +169,6 @@ fn with_appropriate_checker(cx: Context, id: node_id, let id = ast_util::def_id_of_def(fv.def).node; let var_t = ty::node_id_to_type(cx.tcx, id); - // FIXME(#3569): Once closure capabilities are restricted based on their - // incoming bounds, make this check conditional based on the bounds. - if !check_owned(cx, var_t, fv.span) { return; } - // check that only immutable variables are implicitly copied in check_imm_free_var(cx, fv.def, fv.span); @@ -184,10 +180,6 @@ fn with_appropriate_checker(cx: Context, id: node_id, let id = ast_util::def_id_of_def(fv.def).node; let var_t = ty::node_id_to_type(cx.tcx, id); - // FIXME(#3569): Once closure capabilities are restricted based on their - // incoming bounds, make this check conditional based on the bounds. - if !check_durable(cx.tcx, var_t, fv.span) { return; } - // check that only immutable variables are implicitly copied in check_imm_free_var(cx, fv.def, fv.span); @@ -293,9 +285,9 @@ pub fn check_expr(e: @expr, (cx, v): (Context, visit::vt)) { expr_cast(source, _) => { check_cast_for_escaping_regions(cx, source, e); match ty::get(ty::expr_ty(cx.tcx, e)).sty { - ty::ty_trait(_, _, store, _, bounds) => { + ty::ty_trait(_, _, _, _, bounds) => { let source_ty = ty::expr_ty(cx.tcx, source); - check_trait_cast_bounds(cx, e.span, source_ty, bounds, store) + check_trait_cast_bounds(cx, e.span, source_ty, bounds) } _ => { } } @@ -391,7 +383,7 @@ pub fn check_freevar_bounds(cx: Context, sp: span, ty: ty::t, } pub fn check_trait_cast_bounds(cx: Context, sp: span, ty: ty::t, - bounds: ty::BuiltinBounds, store: ty::TraitStore) { + bounds: ty::BuiltinBounds) { do check_builtin_bounds(cx, ty, bounds) |missing| { cx.tcx.sess.span_err(sp, fmt!("cannot pack type `%s`, which does not fulfill \ @@ -399,11 +391,6 @@ pub fn check_trait_cast_bounds(cx: Context, sp: span, ty: ty::t, ty_to_str(cx.tcx, ty), missing.user_string(cx.tcx), bounds.user_string(cx.tcx))); } - // FIXME(#3569): Remove this check when the corresponding restriction - // is made with type contents. - if store == ty::UniqTraitStore && !ty::type_is_owned(cx.tcx, ty) { - cx.tcx.sess.span_err(sp, "uniquely-owned trait objects must be sendable"); - } } fn is_nullary_variant(cx: Context, ex: @expr) -> bool { diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index a8626ca89aa89..e06fd8f971791 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -4195,15 +4195,19 @@ impl Resolver { } } - for bounds.iter().advance |bound| { - self.resolve_type_parameter_bound(bound, visitor); - } + do bounds.map |bound_vec| { + for bound_vec.iter().advance |bound| { + self.resolve_type_parameter_bound(bound, visitor); + } + }; } ty_closure(c) => { - for c.bounds.iter().advance |bound| { - self.resolve_type_parameter_bound(bound, visitor); - } + do c.bounds.map |bounds| { + for bounds.iter().advance |bound| { + self.resolve_type_parameter_bound(bound, visitor); + } + }; visit_ty(ty, ((), visitor)); } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 328b331ea63ed..e6a1ab212526d 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2063,20 +2063,8 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { TC_MANAGED + statically_sized(nonowned(tc_mt(cx, mt, cache))) } - ty_trait(_, _, UniqTraitStore, _, _bounds) => { - // FIXME(#3569): Make this conditional on the trait's bounds. - TC_NONCOPY_TRAIT + TC_OWNED_POINTER - } - - ty_trait(_, _, BoxTraitStore, mutbl, _bounds) => { - match mutbl { - ast::m_mutbl => TC_MANAGED + TC_MUTABLE, - _ => TC_MANAGED - } - } - - ty_trait(_, _, RegionTraitStore(r), mutbl, _bounds) => { - borrowed_contents(r, mutbl) + ty_trait(_, _, store, mutbl, bounds) => { + trait_contents(store, mutbl, bounds) } ty_rptr(r, mt) => { @@ -2261,16 +2249,23 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { } fn closure_contents(cty: &ClosureTy) -> TypeContents { + // Closure contents are just like trait contents, but with potentially + // even more stuff. let st = match cty.sigil { - ast::BorrowedSigil => TC_BORROWED_POINTER, - ast::ManagedSigil => TC_MANAGED, - ast::OwnedSigil => if cty.bounds.contains_elem(BoundCopy) { - TC_OWNED_POINTER - } else { - TC_OWNED_POINTER + TC_NONCOPY_TRAIT - } + ast::BorrowedSigil => + trait_contents(RegionTraitStore(cty.region), m_imm, cty.bounds) + + TC_BORROWED_POINTER, // might be an env packet even if static + ast::ManagedSigil => + trait_contents(BoxTraitStore, m_imm, cty.bounds), + ast::OwnedSigil => + trait_contents(UniqTraitStore, m_imm, cty.bounds), }; + // FIXME(#3569): This borrowed_contents call should be taken care of in + // trait_contents, after ~Traits and @Traits can have region bounds too. + // This one here is redundant for &fns but important for ~fns and @fns. let rt = borrowed_contents(cty.region, m_imm); + // This also prohibits "@once fn" from being copied, which allows it to + // be called. Neither way really makes much sense. let ot = match cty.onceness { ast::Once => TC_ONCE_CLOSURE, ast::Many => TC_NONE @@ -2278,6 +2273,35 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents { st + rt + ot } + fn trait_contents(store: TraitStore, mutbl: ast::mutability, + bounds: BuiltinBounds) -> TypeContents { + let st = match store { + UniqTraitStore => TC_OWNED_POINTER, + BoxTraitStore => TC_MANAGED, + RegionTraitStore(r) => borrowed_contents(r, mutbl), + }; + let mt = match mutbl { ast::m_mutbl => TC_MUTABLE, _ => TC_NONE }; + // We get additional "special type contents" for each bound that *isn't* + // on the trait. So iterate over the inverse of the bounds that are set. + // This is like with typarams below, but less "pessimistic" and also + // dependent on the trait store. + let mut bt = TC_NONE; + for (AllBuiltinBounds() - bounds).each |bound| { + bt = bt + match bound { + BoundCopy if store == UniqTraitStore + => TC_NONCOPY_TRAIT, + BoundCopy => TC_NONE, // @Trait/&Trait are copyable either way + BoundStatic if bounds.contains_elem(BoundOwned) + => TC_NONE, // Owned bound implies static bound. + BoundStatic => TC_BORROWED_POINTER, // Useful for "@Trait:'static" + BoundOwned => TC_NON_OWNED, + BoundConst => TC_MUTABLE, + BoundSized => TC_NONE, // don't care if interior is sized + }; + } + st + mt + bt + } + fn type_param_def_to_contents(cx: ctxt, type_param_def: &TypeParameterDef) -> TypeContents { @@ -4497,7 +4521,9 @@ pub fn visitor_object_ty(tcx: ctxt) -> (@TraitRef, t) { }; let trait_lang_item = tcx.lang_items.ty_visitor(); let trait_ref = @TraitRef { def_id: trait_lang_item, substs: substs }; + let mut static_trait_bound = EmptyBuiltinBounds(); + static_trait_bound.add(BoundStatic); (trait_ref, mk_trait(tcx, trait_ref.def_id, copy trait_ref.substs, - BoxTraitStore, ast::m_imm, EmptyBuiltinBounds())) + BoxTraitStore, ast::m_imm, static_trait_bound)) } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 5e6574850f1b8..997061a61ba63 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -303,7 +303,7 @@ pub fn ast_ty_to_ty( ty::BoxTraitStore } }; - let bounds = conv_builtin_bounds(this.tcx(), bounds); + let bounds = conv_builtin_bounds(this.tcx(), bounds, trait_store); return ty::mk_trait(tcx, result.def_id, copy result.substs, @@ -386,7 +386,13 @@ pub fn ast_ty_to_ty( bf.abis, &bf.lifetimes, &bf.decl)) } ast::ty_closure(ref f) => { - let bounds = conv_builtin_bounds(this.tcx(), &f.bounds); + let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, match f.sigil { + // Use corresponding trait store to figure out default bounds + // if none were specified. + ast::BorrowedSigil => ty::RegionTraitStore(ty::re_empty), // dummy region + ast::OwnedSigil => ty::UniqTraitStore, + ast::ManagedSigil => ty::BoxTraitStore, + }); let fn_decl = ty_of_closure(this, rscope, f.sigil, @@ -411,7 +417,7 @@ pub fn ast_ty_to_ty( match a_def { // But don't emit the error if the user meant to do a trait anyway. ast::def_trait(*) => { }, - _ if !bounds.is_empty() => + _ if bounds.is_some() => tcx.sess.span_err(ast_ty.span, "kind bounds can only be used on trait types"), _ => { }, @@ -741,8 +747,8 @@ pub fn ty_of_closure( } } -fn conv_builtin_bounds(tcx: ty::ctxt, - ast_bounds: &OptVec) +fn conv_builtin_bounds(tcx: ty::ctxt, ast_bounds: &Option>, + store: ty::TraitStore) -> ty::BuiltinBounds { //! Converts a list of bounds from the AST into a `BuiltinBounds` //! struct. Reports an error if any of the bounds that appear @@ -750,32 +756,51 @@ fn conv_builtin_bounds(tcx: ty::ctxt, //! like `Copy` or `Owned`. Used to translate the bounds that //! appear in closure and trait types, where only builtin bounds are //! legal. - - let mut builtin_bounds = ty::EmptyBuiltinBounds(); - for ast_bounds.iter().advance |ast_bound| { - match *ast_bound { - ast::TraitTyParamBound(b) => { - match lookup_def_tcx(tcx, b.path.span, b.ref_id) { - ast::def_trait(trait_did) => { - if try_add_builtin_trait(tcx, - trait_did, - &mut builtin_bounds) { - loop; // success + //! If no bounds were specified, we choose a "default" bound based on + //! the allocation type of the fn/trait, as per issue #7264. The user can + //! override this with an empty bounds list, e.g. "~fn:()" or "~Trait:". + + match (ast_bounds, store) { + (&Some(ref bound_vec), _) => { + let mut builtin_bounds = ty::EmptyBuiltinBounds(); + for bound_vec.iter().advance |ast_bound| { + match *ast_bound { + ast::TraitTyParamBound(b) => { + match lookup_def_tcx(tcx, b.path.span, b.ref_id) { + ast::def_trait(trait_did) => { + if try_add_builtin_trait(tcx, + trait_did, + &mut builtin_bounds) { + loop; // success + } + } + _ => { } } + tcx.sess.span_fatal( + b.path.span, + fmt!("only the builtin traits can be used \ + as closure or object bounds")); + } + ast::RegionTyParamBound => { + builtin_bounds.add(ty::BoundStatic); } - _ => { } } - tcx.sess.span_fatal( - b.path.span, - fmt!("only the builtin traits can be used \ - as closure or object bounds")); - } - ast::RegionTyParamBound => { - builtin_bounds.add(ty::BoundStatic); } + builtin_bounds + }, + // ~Trait is sugar for ~Trait:Owned. + (&None, ty::UniqTraitStore) => { + let mut set = ty::EmptyBuiltinBounds(); set.add(ty::BoundOwned); set + } + // @Trait is sugar for @Trait:'static. + // &'static Trait is sugar for &'static Trait:'static. + (&None, ty::BoxTraitStore) | + (&None, ty::RegionTraitStore(ty::re_static)) => { + let mut set = ty::EmptyBuiltinBounds(); set.add(ty::BoundStatic); set } + // &'r Trait is sugar for &'r Trait:. + (&None, ty::RegionTraitStore(*)) => ty::EmptyBuiltinBounds(), } - builtin_bounds } pub fn try_add_builtin_trait(tcx: ty::ctxt, diff --git a/src/libstd/io.rs b/src/libstd/io.rs index 71a0dd6b9b2ab..a78be9c8b2b1d 100644 --- a/src/libstd/io.rs +++ b/src/libstd/io.rs @@ -1042,12 +1042,14 @@ pub fn file_reader(path: &Path) -> Result<@Reader, ~str> { // Byte readers -pub struct BytesReader<'self> { - bytes: &'self [u8], +pub struct BytesReader { + // FIXME(#5723) see other FIXME below + // FIXME(#7268) this should also be parameterized over <'self> + bytes: &'static [u8], pos: @mut uint } -impl<'self> Reader for BytesReader<'self> { +impl Reader for BytesReader { fn read(&self, bytes: &mut [u8], len: uint) -> uint { let count = uint::min(len, self.bytes.len() - *self.pos); @@ -1084,6 +1086,10 @@ impl<'self> Reader for BytesReader<'self> { } pub fn with_bytes_reader(bytes: &[u8], f: &fn(@Reader) -> T) -> T { + // XXX XXX XXX this is glaringly unsound + // FIXME(#5723) Use a &Reader for the callback's argument. Should be: + // fn with_bytes_reader<'r, T>(bytes: &'r [u8], f: &fn(&'r Reader) -> T) -> T + let bytes: &'static [u8] = unsafe { cast::transmute(bytes) }; f(@BytesReader { bytes: bytes, pos: @mut 0 @@ -1091,6 +1097,7 @@ pub fn with_bytes_reader(bytes: &[u8], f: &fn(@Reader) -> T) -> T { } pub fn with_str_reader(s: &str, f: &fn(@Reader) -> T) -> T { + // FIXME(#5723): As above. with_bytes_reader(s.as_bytes(), f) } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 90b2f131b00ee..1a7094acf7e4e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -771,7 +771,11 @@ pub struct TyClosure { purity: purity, onceness: Onceness, decl: fn_decl, - bounds: OptVec + // Optional optvec distinguishes between "fn()" and "fn:()" so we can + // implement issue #7264. None means "fn()", which means infer a default + // bound based on pointer sigil during typeck. Some(Empty) means "fn:()", + // which means use no bounds (e.g., not even Owned on a ~fn()). + bounds: Option>, } #[deriving(Eq, Encodable, Decodable)] @@ -795,7 +799,7 @@ pub enum ty_ { ty_closure(@TyClosure), ty_bare_fn(@TyBareFn), ty_tup(~[@Ty]), - ty_path(@Path, @OptVec, node_id), + ty_path(@Path, @Option>, node_id), // for #7264; see above ty_mac(mac), // ty_infer means the type should be inferred instead of it having been // specified. This should only appear at the "top level" of a type and not diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 02d5991e1a7bb..2c1b4cfc59155 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -46,7 +46,7 @@ pub trait AstBuilder { fn ty_mt(&self, ty: @ast::Ty, mutbl: ast::mutability) -> ast::mt; fn ty(&self, span: span, ty: ast::ty_) -> @ast::Ty; - fn ty_path(&self, @ast::Path, @OptVec) -> @ast::Ty; + fn ty_path(&self, @ast::Path, @Option>) -> @ast::Ty; fn ty_ident(&self, span: span, idents: ast::ident) -> @ast::Ty; fn ty_rptr(&self, span: span, @@ -265,7 +265,7 @@ impl AstBuilder for @ExtCtxt { } } - fn ty_path(&self, path: @ast::Path, bounds: @OptVec) + fn ty_path(&self, path: @ast::Path, bounds: @Option>) -> @ast::Ty { self.ty(path.span, ast::ty_path(path, bounds, self.next_id())) @@ -275,7 +275,7 @@ impl AstBuilder for @ExtCtxt { // to generate a bounded existential trait type. fn ty_ident(&self, span: span, ident: ast::ident) -> @ast::Ty { - self.ty_path(self.path_ident(span, ident), @opt_vec::Empty) + self.ty_path(self.path_ident(span, ident), @None) } fn ty_rptr(&self, @@ -306,7 +306,7 @@ impl AstBuilder for @ExtCtxt { ], None, ~[ ty ]), - @opt_vec::Empty) + @None) } fn ty_field_imm(&self, span: span, name: ident, ty: @ast::Ty) -> ast::ty_field { @@ -344,7 +344,7 @@ impl AstBuilder for @ExtCtxt { fn ty_vars_global(&self, ty_params: &OptVec) -> ~[@ast::Ty] { opt_vec::take_vec( ty_params.map(|p| self.ty_path( - self.path_global(dummy_sp(), ~[p.ident]), @opt_vec::Empty))) + self.path_global(dummy_sp(), ~[p.ident]), @None))) } fn strip_bounds(&self, generics: &Generics) -> Generics { diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 17bbe7a333e65..10d9f878bc451 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -357,7 +357,7 @@ impl<'self> TraitDef<'self> { // Create the type of `self`. let self_type = cx.ty_path(cx.path_all(span, false, ~[ type_ident ], self_lifetime, opt_vec::take_vec(self_ty_params)), - @opt_vec::Empty); + @None); let doc_attr = cx.attribute( span, diff --git a/src/libsyntax/ext/deriving/ty.rs b/src/libsyntax/ext/deriving/ty.rs index 9da6bf27eadae..2f21eba11d7e2 100644 --- a/src/libsyntax/ext/deriving/ty.rs +++ b/src/libsyntax/ext/deriving/ty.rs @@ -63,7 +63,7 @@ impl<'self> Path<'self> { self_generics: &Generics) -> @ast::Ty { cx.ty_path(self.to_path(cx, span, - self_ty, self_generics), @opt_vec::Empty) + self_ty, self_generics), @None) } pub fn to_path(&self, cx: @ExtCtxt, @@ -143,7 +143,7 @@ impl<'self> Ty<'self> { Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) } Self => { cx.ty_path(self.to_path(cx, span, self_ty, self_generics), - @opt_vec::Empty) + @None) } Tuple(ref fields) => { let ty = if fields.is_empty() { diff --git a/src/libsyntax/ext/pipes/pipec.rs b/src/libsyntax/ext/pipes/pipec.rs index a75a20a0fa97c..2c5ec0909d951 100644 --- a/src/libsyntax/ext/pipes/pipec.rs +++ b/src/libsyntax/ext/pipes/pipec.rs @@ -59,7 +59,7 @@ impl gen_send for message { let pipe_ty = cx.ty_path( path(~[this.data_name()], span) - .add_tys(cx.ty_vars(&this.generics.ty_params)), @opt_vec::Empty); + .add_tys(cx.ty_vars(&this.generics.ty_params)), @None); let args_ast = vec::append( ~[cx.arg(span, cx.ident_of("pipe"), pipe_ty)], args_ast); @@ -115,7 +115,7 @@ impl gen_send for message { let mut rty = cx.ty_path(path(~[next.data_name()], span) - .add_tys(copy next_state.tys), @opt_vec::Empty); + .add_tys(copy next_state.tys), @None); if try { rty = cx.ty_option(rty); } @@ -144,7 +144,7 @@ impl gen_send for message { cx.ty_path( path(~[this.data_name()], span) .add_tys(cx.ty_vars( - &this.generics.ty_params)), @opt_vec::Empty))], + &this.generics.ty_params)), @None))], args_ast); let message_args = if arg_names.len() == 0 { @@ -190,7 +190,7 @@ impl gen_send for message { fn to_ty(&mut self, cx: @ExtCtxt) -> @ast::Ty { cx.ty_path(path(~[cx.ident_of(self.name())], self.span()) - .add_tys(cx.ty_vars(&self.get_generics().ty_params)), @opt_vec::Empty) + .add_tys(cx.ty_vars(&self.get_generics().ty_params)), @None) } } @@ -224,7 +224,7 @@ impl to_type_decls for state { cx.ty_path( path(~[cx.ident_of(dir), cx.ident_of(next_name)], span) - .add_tys(copy next_state.tys), @opt_vec::Empty)) + .add_tys(copy next_state.tys), @None)) } None => tys }; @@ -277,8 +277,8 @@ impl to_type_decls for state { self.data_name()], dummy_sp()) .add_tys(cx.ty_vars( - &self.generics.ty_params)), @opt_vec::Empty)), - @opt_vec::Empty), + &self.generics.ty_params)), @None)), + @None), cx.strip_bounds(&self.generics))); } else { @@ -297,8 +297,8 @@ impl to_type_decls for state { self.data_name()], dummy_sp()) .add_tys(cx.ty_vars_global( - &self.generics.ty_params)), @opt_vec::Empty), - self.proto.buffer_ty_path(cx)]), @opt_vec::Empty), + &self.generics.ty_params)), @None), + self.proto.buffer_ty_path(cx)]), @None), cx.strip_bounds(&self.generics))); }; items @@ -383,7 +383,7 @@ impl gen_init for protocol { cx.ty_path(path(~[cx.ident_of("super"), cx.ident_of("__Buffer")], copy self.span) - .add_tys(cx.ty_vars_global(¶ms)), @opt_vec::Empty) + .add_tys(cx.ty_vars_global(¶ms)), @None) } fn gen_buffer_type(&self, cx: @ExtCtxt) -> @ast::item { diff --git a/src/libsyntax/ext/pipes/proto.rs b/src/libsyntax/ext/pipes/proto.rs index e86ced847d74a..0525c6664780e 100644 --- a/src/libsyntax/ext/pipes/proto.rs +++ b/src/libsyntax/ext/pipes/proto.rs @@ -13,7 +13,6 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::pipes::ast_builder::{append_types, path}; -use opt_vec; #[deriving(Eq)] pub enum direction { send, recv } @@ -100,7 +99,7 @@ impl state_ { pub fn to_ty(&self, cx: @ExtCtxt) -> @ast::Ty { cx.ty_path (path(~[cx.ident_of(self.name)],self.span).add_tys( - cx.ty_vars(&self.generics.ty_params)), @opt_vec::Empty) + cx.ty_vars(&self.generics.ty_params)), @None) } /// Iterate over the states that can be reached in one message diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 2c7c335a84109..2fc111da453cf 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -651,6 +651,12 @@ pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ { span: fld.new_span(f.span), } } + fn fold_opt_bounds(b: &Option>, fld: @ast_fold) + -> Option> { + do b.map |bounds| { + do bounds.map |bound| { fold_ty_param_bound(bound, fld) } + } + } match *t { ty_nil | ty_bot | ty_infer => copy *t, ty_box(ref mt) => ty_box(fold_mt(mt, fld)), @@ -664,7 +670,7 @@ pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ { purity: f.purity, region: f.region, onceness: f.onceness, - bounds: f.bounds.map(|x| fold_ty_param_bound(x, fld)), + bounds: fold_opt_bounds(&f.bounds, fld), decl: fold_fn_decl(&f.decl, fld), lifetimes: copy f.lifetimes, }) @@ -679,8 +685,7 @@ pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ { } ty_tup(ref tys) => ty_tup(tys.map(|ty| fld.fold_ty(*ty))), ty_path(path, bounds, id) => - ty_path(fld.fold_path(path), - @bounds.map(|x| fold_ty_param_bound(x, fld)), fld.new_id(id)), + ty_path(fld.fold_path(path), @fold_opt_bounds(bounds, fld), fld.new_id(id)), ty_fixed_length_vec(ref mt, e) => { ty_fixed_length_vec( fold_mt(mt, fld), diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index d54c64e7ee60e..6dd8d4880e3f0 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -490,7 +490,7 @@ mod test { idents:~[str_to_ident("int")], rp: None, types: ~[]}, - @opt_vec::Empty, 2), + @None, 2), span:sp(4,7)}, pat: @ast::pat{id:1, node: ast::pat_ident(ast::bind_infer, @@ -526,7 +526,7 @@ mod test { idents:~[str_to_ident("int")], rp: None, types: ~[]}, - @opt_vec::Empty, 2), + @None, 2), span:sp(10,13)}, pat: @ast::pat{id:1, // fixme node: ast::pat_ident( diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b10005aeaf1a0..d67771fc43547 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1047,8 +1047,8 @@ impl Parser { // Like the above, but can also parse kind bounds in the case of a // path to be used as a type that might be a trait. - pub fn parse_type_path(&self) -> (@ast::Path, OptVec) { - let mut bounds = opt_vec::Empty; + pub fn parse_type_path(&self) -> (@ast::Path, Option>) { + let mut bounds = None; let path = self.parse_bounded_path_with_tps(false, Some(|| { // Note: this closure might not even get called in the case of a // macro-generated path. But that's the macro parser's job. @@ -2884,9 +2884,13 @@ impl Parser { // matches optbounds = ( ( : ( boundseq )? )? ) // where boundseq = ( bound + boundseq ) | bound // and bound = 'static | ty - fn parse_optional_ty_param_bounds(&self) -> OptVec { + // Returns "None" if there's no colon (e.g. "T"); + // Returns "Some(Empty)" if there's a colon but nothing after (e.g. "T:") + // Returns "Some(stuff)" otherwise (e.g. "T:stuff"). + // NB: The None/Some distinction is important for issue #7264. + fn parse_optional_ty_param_bounds(&self) -> Option> { if !self.eat(&token::COLON) { - return opt_vec::Empty; + return None; } let mut result = opt_vec::Empty; @@ -2935,13 +2939,15 @@ impl Parser { } } - return result; + return Some(result); } // matches typaram = IDENT optbounds fn parse_ty_param(&self) -> TyParam { let ident = self.parse_ident(); - let bounds = @self.parse_optional_ty_param_bounds(); + let opt_bounds = self.parse_optional_ty_param_bounds(); + // For typarams we don't care about the difference b/w "" and "". + let bounds = @opt_bounds.get_or_default(opt_vec::Empty); ast::TyParam { ident: ident, id: self.get_id(), bounds: bounds } } @@ -3288,7 +3294,7 @@ impl Parser { let opt_trait = if could_be_trait && self.eat_keyword(keywords::For) { // New-style trait. Reinterpret the type as a trait. let opt_trait_ref = match ty.node { - ty_path(path, @opt_vec::Empty, node_id) => { + ty_path(path, @None, node_id) => { Some(@trait_ref { path: path, ref_id: node_id diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 277cc6208fb08..728a5a3d32af2 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -410,14 +410,14 @@ pub fn print_type(s: @ps, ty: @ast::Ty) { let generics = ast::Generics {lifetimes: copy f.lifetimes, ty_params: opt_vec::Empty}; print_ty_fn(s, Some(f.abis), None, None, - f.purity, ast::Many, &f.decl, None, + f.purity, ast::Many, &f.decl, None, &None, Some(&generics), None); } ast::ty_closure(f) => { let generics = ast::Generics {lifetimes: copy f.lifetimes, ty_params: opt_vec::Empty}; print_ty_fn(s, None, Some(f.sigil), f.region, - f.purity, f.onceness, &f.decl, None, + f.purity, f.onceness, &f.decl, None, &f.bounds, Some(&generics), None); } ast::ty_path(path, bounds, _) => print_bounded_path(s, path, bounds), @@ -806,7 +806,7 @@ pub fn print_ty_method(s: @ps, m: &ast::ty_method) { maybe_print_comment(s, m.span.lo); print_outer_attributes(s, m.attrs); print_ty_fn(s, None, None, None, m.purity, ast::Many, - &m.decl, Some(m.ident), Some(&m.generics), + &m.decl, Some(m.ident), &None, Some(&m.generics), Some(/*bad*/ copy m.explicit_self.node)); word(s.s, ";"); } @@ -1488,7 +1488,7 @@ pub fn print_for_decl(s: @ps, loc: @ast::local, coll: @ast::expr) { } fn print_path_(s: @ps, path: @ast::Path, colons_before_params: bool, - opt_bounds: Option<@OptVec>) { + opt_bounds: &Option>) { maybe_print_comment(s, path.span.lo); if path.global { word(s.s, "::"); } let mut first = true; @@ -1496,8 +1496,8 @@ fn print_path_(s: @ps, path: @ast::Path, colons_before_params: bool, if first { first = false; } else { word(s.s, "::"); } print_ident(s, *id); } - do opt_bounds.map_consume |bounds| { - print_bounds(s, bounds); + do opt_bounds.map |bounds| { + print_bounds(s, bounds, true); }; if path.rp.is_some() || !path.types.is_empty() { if colons_before_params { word(s.s, "::"); } @@ -1520,12 +1520,12 @@ fn print_path_(s: @ps, path: @ast::Path, colons_before_params: bool, } pub fn print_path(s: @ps, path: @ast::Path, colons_before_params: bool) { - print_path_(s, path, colons_before_params, None) + print_path_(s, path, colons_before_params, &None) } pub fn print_bounded_path(s: @ps, path: @ast::Path, - bounds: @OptVec) { - print_path_(s, path, false, Some(bounds)) + bounds: &Option>) { + print_path_(s, path, false, bounds) } pub fn print_irrefutable_pat(s: @ps, pat: @ast::pat) { @@ -1737,7 +1737,8 @@ pub fn print_fn_block_args(s: @ps, decl: &ast::fn_decl) { maybe_print_comment(s, decl.output.span.lo); } -pub fn print_bounds(s: @ps, bounds: @OptVec) { +pub fn print_bounds(s: @ps, bounds: &OptVec, + print_colon_anyway: bool) { if !bounds.is_empty() { word(s.s, ":"); let mut first = true; @@ -1754,6 +1755,8 @@ pub fn print_bounds(s: @ps, bounds: @OptVec) { RegionTyParamBound => word(s.s, "'static"), } } + } else if print_colon_anyway { + word(s.s, ":"); } } @@ -1774,7 +1777,7 @@ pub fn print_generics(s: @ps, generics: &ast::Generics) { let idx = idx - generics.lifetimes.len(); let param = generics.ty_params.get(idx); print_ident(s, param.ident); - print_bounds(s, param.bounds); + print_bounds(s, param.bounds, false); } } @@ -1917,6 +1920,7 @@ pub fn print_ty_fn(s: @ps, onceness: ast::Onceness, decl: &ast::fn_decl, id: Option, + opt_bounds: &Option>, generics: Option<&ast::Generics>, opt_explicit_self: Option) { ibox(s, indent_unit); @@ -1930,6 +1934,7 @@ pub fn print_ty_fn(s: @ps, print_onceness(s, onceness); word(s.s, "fn"); match id { Some(id) => { word(s.s, " "); print_ident(s, id); } _ => () } + do opt_bounds.map |bounds| { print_bounds(s, bounds, true); }; match generics { Some(g) => print_generics(s, g), _ => () } zerobreak(s.s); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 9b816ce5e5b7c..f0a993dbb9423 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -246,7 +246,9 @@ pub fn visit_ty(t: @Ty, (e, v): (E, vt)) { ty_closure(ref f) => { for f.decl.inputs.iter().advance |a| { (v.visit_ty)(a.ty, (copy e, v)); } (v.visit_ty)(f.decl.output, (copy e, v)); - visit_ty_param_bounds(&f.bounds, (e, v)); + do f.bounds.map |bounds| { + visit_ty_param_bounds(bounds, (copy e, v)); + }; }, ty_bare_fn(ref f) => { for f.decl.inputs.iter().advance |a| { (v.visit_ty)(a.ty, (copy e, v)); } @@ -254,7 +256,9 @@ pub fn visit_ty(t: @Ty, (e, v): (E, vt)) { }, ty_path(p, bounds, _) => { visit_path(p, (copy e, v)); - visit_ty_param_bounds(bounds, (e, v)); + do bounds.map |bounds| { + visit_ty_param_bounds(bounds, (copy e, v)); + }; }, ty_fixed_length_vec(ref mt, ex) => { (v.visit_ty)(mt.ty, (copy e, v)); diff --git a/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs index c3c8467233c4d..098a395f01744 100644 --- a/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs +++ b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs @@ -12,7 +12,7 @@ struct X { field: @fn:Copy(), } -fn foo(blk: @fn()) -> X { +fn foo(blk: @fn:()) -> X { return X { field: blk }; //~ ERROR expected bounds `Copy` but found no bounds } diff --git a/src/test/compile-fail/closure-bounds-subtype.rs b/src/test/compile-fail/closure-bounds-subtype.rs index a975349e73022..887346e35e5ea 100644 --- a/src/test/compile-fail/closure-bounds-subtype.rs +++ b/src/test/compile-fail/closure-bounds-subtype.rs @@ -1,5 +1,5 @@ -fn take_any(_: &fn()) { +fn take_any(_: &fn:()) { } fn take_copyable(_: &fn:Copy()) { @@ -11,7 +11,7 @@ fn take_copyable_owned(_: &fn:Copy+Owned()) { fn take_const_owned(_: &fn:Const+Owned()) { } -fn give_any(f: &fn()) { +fn give_any(f: &fn:()) { take_any(f); take_copyable(f); //~ ERROR expected bounds `Copy` but found no bounds take_copyable_owned(f); //~ ERROR expected bounds `Copy+Owned` but found no bounds diff --git a/src/test/compile-fail/kindck-nonsendable-1.rs b/src/test/compile-fail/kindck-nonsendable-1.rs index 928abae242383..38983a9aca6a0 100644 --- a/src/test/compile-fail/kindck-nonsendable-1.rs +++ b/src/test/compile-fail/kindck-nonsendable-1.rs @@ -12,7 +12,7 @@ fn foo(_x: @uint) {} fn main() { let x = @3u; - let _: ~fn() = || foo(x); //~ ERROR value has non-owned type `@uint` - let _: ~fn() = || foo(x); //~ ERROR value has non-owned type `@uint` - let _: ~fn() = || foo(x); //~ ERROR value has non-owned type `@uint` + let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Owned` + let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Owned` + let _: ~fn() = || foo(x); //~ ERROR does not fulfill `Owned` } diff --git a/src/test/compile-fail/kindck-owned-trait.rs b/src/test/compile-fail/kindck-owned-trait.rs index 857c71db72ce0..c2352e35a46e9 100644 --- a/src/test/compile-fail/kindck-owned-trait.rs +++ b/src/test/compile-fail/kindck-owned-trait.rs @@ -11,7 +11,9 @@ trait foo { fn foo(&self); } fn to_foo(t: T) -> @foo { - @t as @foo //~ ERROR value may contain borrowed pointers; add `'static` bound + @t as @foo + //~^ ERROR value may contain borrowed pointers; add `'static` bound + //~^^ ERROR cannot pack type } fn to_foo2(t: T) -> @foo { diff --git a/src/test/compile-fail/kindck-owned.rs b/src/test/compile-fail/kindck-owned.rs index ec84551f7b0a5..3f859b7dc84e6 100644 --- a/src/test/compile-fail/kindck-owned.rs +++ b/src/test/compile-fail/kindck-owned.rs @@ -9,8 +9,7 @@ // except according to those terms. fn copy1(t: T) -> @fn() -> T { - let result: @fn() -> T = || copy t; - //~^ ERROR value may contain borrowed pointers + let result: @fn() -> T = || copy t; //~ ERROR does not fulfill `'static` result } diff --git a/src/test/compile-fail/no-send-res-ports.rs b/src/test/compile-fail/no-send-res-ports.rs index 1f5b600157f2d..605e59d56c893 100644 --- a/src/test/compile-fail/no-send-res-ports.rs +++ b/src/test/compile-fail/no-send-res-ports.rs @@ -32,7 +32,7 @@ fn main() { let x = Cell::new(foo(Port(@()))); do task::spawn { - let y = x.take(); //~ ERROR value has non-owned type + let y = x.take(); //~ ERROR does not fulfill `Owned` error!(y); } } diff --git a/src/test/compile-fail/trait-bounds-cant-coerce.rs b/src/test/compile-fail/trait-bounds-cant-coerce.rs index 88c2d49174733..adaea1de9bd0f 100644 --- a/src/test/compile-fail/trait-bounds-cant-coerce.rs +++ b/src/test/compile-fail/trait-bounds-cant-coerce.rs @@ -21,7 +21,7 @@ fn c(x: ~Foo:Const+Owned) { b(x); //~ ERROR expected bounds `Copy+Owned` } -fn d(x: ~Foo) { +fn d(x: ~Foo:) { a(x); //~ ERROR found no bounds } diff --git a/src/test/compile-fail/trait-bounds-sugar.rs b/src/test/compile-fail/trait-bounds-sugar.rs new file mode 100644 index 0000000000000..8c641f4c850d7 --- /dev/null +++ b/src/test/compile-fail/trait-bounds-sugar.rs @@ -0,0 +1,37 @@ +// Copyright 2013 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. + +// Tests for "default" bounds inferred for traits with no bounds list. + +trait Foo { +} + +fn a(_x: ~Foo) { // should be same as ~Foo:Owned +} + +fn b(_x: @Foo) { // should be same as ~Foo:'static +} + +fn c(_x: &'static Foo) { // should be same as &'static Foo:'static +} + +fn d(x: ~Foo:Const) { + a(x); //~ ERROR expected bounds `Owned` +} + +fn e(x: @Foo:Const) { + b(x); //~ ERROR expected bounds `'static` +} + +fn f(x: &'static Foo:Const) { + c(x); //~ ERROR expected bounds `'static` +} + +fn main() { } diff --git a/src/test/run-pass/closure-bounds-copyable-squiggle-closure.rs b/src/test/run-pass/closure-bounds-copyable-squiggle-closure.rs index f39c914916ff7..8c2ae22e8edfc 100644 --- a/src/test/run-pass/closure-bounds-copyable-squiggle-closure.rs +++ b/src/test/run-pass/closure-bounds-copyable-squiggle-closure.rs @@ -8,11 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-pretty - // Tests correct copying of heap closures' environments. -fn foo(x: ~fn:Copy()) -> (~fn(), ~fn()) { +fn foo(x: ~fn:Copy()) -> (~fn:(), ~fn:()) { (copy x, x) } fn main() { diff --git a/src/test/run-pass/closure-bounds-squiggle-closure-as-copyable-typaram.rs b/src/test/run-pass/closure-bounds-squiggle-closure-as-copyable-typaram.rs index 2fdce4e5c7cdf..88d474a51e1da 100644 --- a/src/test/run-pass/closure-bounds-squiggle-closure-as-copyable-typaram.rs +++ b/src/test/run-pass/closure-bounds-squiggle-closure-as-copyable-typaram.rs @@ -8,14 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-pretty - // Tests correct copying of heap closures' environments. fn bar(x: T) -> (T, T) { (copy x, x) } -fn foo(x: ~fn:Copy()) -> (~fn(), ~fn()) { +fn foo(x: ~fn:Copy()) -> (~fn:(), ~fn:()) { bar(x) } fn main() { diff --git a/src/test/run-pass/kindck-owned-trait-contains-1.rs b/src/test/run-pass/kindck-owned-trait-contains-1.rs index c51094d26c88f..e733400527b28 100644 --- a/src/test/run-pass/kindck-owned-trait-contains-1.rs +++ b/src/test/run-pass/kindck-owned-trait-contains-1.rs @@ -14,9 +14,9 @@ impl repeat for @A { fn get(&self) -> A { copy **self } } -fn repeater(v: @A) -> @repeat { +fn repeater(v: @A) -> @repeat: { // Note: owned kind is not necessary as A appears in the trait type - @v as @repeat // No + @v as @repeat: // No } pub fn main() { diff --git a/src/test/run-pass/regions-trait.rs b/src/test/run-pass/regions-trait.rs index b5b13efa634fa..049d5305ca427 100644 --- a/src/test/run-pass/regions-trait.rs +++ b/src/test/run-pass/regions-trait.rs @@ -22,7 +22,7 @@ impl<'self> get_ctxt<'self> for HasCtxt<'self> { } } -fn get_v(gc: @get_ctxt) -> uint { +fn get_v(gc: @get_ctxt:) -> uint { gc.get_ctxt().v } @@ -30,5 +30,5 @@ pub fn main() { let ctxt = Ctxt { v: 22 }; let hc = HasCtxt { c: &ctxt }; - assert_eq!(get_v(@hc as @get_ctxt), 22); + assert_eq!(get_v(@hc as @get_ctxt:), 22); } diff --git a/src/test/run-pass/trait-bounds-basic.rs b/src/test/run-pass/trait-bounds-basic.rs index b9251c038afaa..5bfbf84d8acac 100644 --- a/src/test/run-pass/trait-bounds-basic.rs +++ b/src/test/run-pass/trait-bounds-basic.rs @@ -11,7 +11,7 @@ trait Foo { } -fn a(_x: ~Foo) { +fn a(_x: ~Foo:) { } fn b(_x: ~Foo:Owned) { @@ -25,4 +25,8 @@ fn d(x: ~Foo:Owned+Copy) { b(x); } +fn e(x: ~Foo) { // sugar for ~Foo:Owned + b(x); +} + fn main() { } diff --git a/src/test/run-pass/trait-bounds-in-arc.rs b/src/test/run-pass/trait-bounds-in-arc.rs new file mode 100644 index 0000000000000..585c2185a7eca --- /dev/null +++ b/src/test/run-pass/trait-bounds-in-arc.rs @@ -0,0 +1,103 @@ +// Copyright 2013 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. + +// Tests that a heterogeneous list of existential types can be put inside an ARC +// and shared between tasks as long as all types fulfill Const+Owned. + +// xfail-fast + +extern mod extra; +use extra::arc; +use std::comm; +use std::task; +use std::cell; + +trait Pet { + fn name(&self, blk: &fn(&str)); + fn num_legs(&self) -> uint; + fn of_good_pedigree(&self) -> bool; +} + +struct Catte { + num_whiskers: uint, + name: ~str, +} + +struct Dogge { + bark_decibels: uint, + tricks_known: uint, + name: ~str, +} + +struct Goldfyshe { + swim_speed: uint, + name: ~str, +} + +impl Pet for Catte { + fn name(&self, blk: &fn(&str)) { blk(self.name) } + fn num_legs(&self) -> uint { 4 } + fn of_good_pedigree(&self) -> bool { self.num_whiskers >= 4 } +} +impl Pet for Dogge { + fn name(&self, blk: &fn(&str)) { blk(self.name) } + fn num_legs(&self) -> uint { 4 } + fn of_good_pedigree(&self) -> bool { + self.bark_decibels < 70 || self.tricks_known > 20 + } +} +impl Pet for Goldfyshe { + fn name(&self, blk: &fn(&str)) { blk(self.name) } + fn num_legs(&self) -> uint { 0 } + fn of_good_pedigree(&self) -> bool { self.swim_speed >= 500 } +} + +fn main() { + let catte = Catte { num_whiskers: 7, name: ~"alonzo_church" }; + let dogge1 = Dogge { bark_decibels: 100, tricks_known: 42, name: ~"alan_turing" }; + let dogge2 = Dogge { bark_decibels: 55, tricks_known: 11, name: ~"albert_einstein" }; + let fishe = Goldfyshe { swim_speed: 998, name: ~"alec_guinness" }; + let arc = arc::ARC(~[~catte as ~Pet:Const+Owned, + ~dogge1 as ~Pet:Const+Owned, + ~fishe as ~Pet:Const+Owned, + ~dogge2 as ~Pet:Const+Owned]); + let (p1,c1) = comm::stream(); + let arc1 = cell::Cell::new(arc.clone()); + do task::spawn { check_legs(arc1.take()); c1.send(()); } + let (p2,c2) = comm::stream(); + let arc2 = cell::Cell::new(arc.clone()); + do task::spawn { check_names(arc2.take()); c2.send(()); } + let (p3,c3) = comm::stream(); + let arc3 = cell::Cell::new(arc.clone()); + do task::spawn { check_pedigree(arc3.take()); c3.send(()); } + p1.recv(); + p2.recv(); + p3.recv(); +} + +fn check_legs(arc: arc::ARC<~[~Pet:Const+Owned]>) { + let mut legs = 0; + for arc.get().iter().advance |pet| { + legs += pet.num_legs(); + } + assert!(legs == 12); +} +fn check_names(arc: arc::ARC<~[~Pet:Const+Owned]>) { + for arc.get().iter().advance |pet| { + do pet.name |name| { + assert!(name[0] == 'a' as u8 && name[1] == 'l' as u8); + } + } +} +fn check_pedigree(arc: arc::ARC<~[~Pet:Const+Owned]>) { + for arc.get().iter().advance |pet| { + assert!(pet.of_good_pedigree()); + } +}