diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 2f19a4ebb6a..d7d16556acd 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -29,6 +29,10 @@ pub use self::fulfill::{FulfillmentContext, RegionObligation}; pub use self::project::MismatchedProjectionTypes; pub use self::project::normalize; pub use self::project::Normalized; +pub use self::object_safety::is_object_safe; +pub use self::object_safety::object_safety_violations; +pub use self::object_safety::ObjectSafetyViolation; +pub use self::object_safety::MethodViolationCode; pub use self::select::SelectionContext; pub use self::select::SelectionCache; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; @@ -45,6 +49,7 @@ mod coherence; mod error_reporting; mod fulfill; mod project; +mod object_safety; mod select; mod util; diff --git a/src/librustc/middle/traits/object_safety.rs b/src/librustc/middle/traits/object_safety.rs new file mode 100644 index 00000000000..20333043795 --- /dev/null +++ b/src/librustc/middle/traits/object_safety.rs @@ -0,0 +1,302 @@ +// 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. + +//! "Object safety" refers to the ability for a trait to be converted +//! to an object. In general, traits may only be converted to an +//! object if all of their methods meet certain criteria. In particular, +//! they must: +//! +//! - have a suitable receiver from which we can extract a vtable; +//! - not reference the erased type `Self` except for in this receiver; +//! - not have generic type parameters + +use super::supertraits; +use super::elaborate_predicates; + +use middle::subst::{mod, SelfSpace}; +use middle::traits; +use middle::ty::{mod, Ty}; +use std::rc::Rc; +use syntax::ast; +use util::ppaux::Repr; + +pub enum ObjectSafetyViolation<'tcx> { + /// Self : Sized declared on the trait + SizedSelf, + + /// Method has someting illegal + Method(Rc>, MethodViolationCode), +} + +/// Reasons a method might not be object-safe. +#[deriving(Copy,Clone,Show)] +pub enum MethodViolationCode { + /// fn(self), + ByValueSelf, + + // fn foo() + StaticMethod, + + // fn foo(&self, x: Self) + // fn foo(&self) -> Self + ReferencesSelf, + + // fn foo(), + Generic, +} + +pub fn is_object_safe<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>) + -> bool +{ + // Because we query yes/no results frequently, we keep a cache: + let cached_result = + tcx.object_safety_cache.borrow().get(&trait_ref.def_id()).map(|&r| r); + + let result = + cached_result.unwrap_or_else(|| { + let result = object_safety_violations(tcx, trait_ref.clone()).is_empty(); + + // Record just a yes/no result in the cache; this is what is + // queried most frequently. Note that this may overwrite a + // previous result, but always with the same thing. + tcx.object_safety_cache.borrow_mut().insert(trait_ref.def_id(), result); + + result + }); + + debug!("is_object_safe({}) = {}", trait_ref.repr(tcx), result); + + result +} + +pub fn object_safety_violations<'tcx>(tcx: &ty::ctxt<'tcx>, + sub_trait_ref: ty::PolyTraitRef<'tcx>) + -> Vec> +{ + supertraits(tcx, sub_trait_ref) + .flat_map(|tr| object_safety_violations_for_trait(tcx, tr.def_id()).into_iter()) + .collect() +} + +fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_def_id: ast::DefId) + -> Vec> +{ + // Check methods for violations. + let mut violations: Vec<_> = + ty::trait_items(tcx, trait_def_id).iter() + .flat_map(|item| { + match *item { + ty::MethodTraitItem(ref m) => { + object_safety_violations_for_method(tcx, trait_def_id, &**m) + .map(|code| ObjectSafetyViolation::Method(m.clone(), code)) + .into_iter() + } + ty::TypeTraitItem(_) => { + None.into_iter() + } + } + }) + .collect(); + + // Check the trait itself. + if trait_has_sized_self(tcx, trait_def_id) { + violations.push(ObjectSafetyViolation::SizedSelf); + } + + debug!("object_safety_violations_for_trait(trait_def_id={}) = {}", + trait_def_id.repr(tcx), + violations.repr(tcx)); + + violations +} + +fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_def_id: ast::DefId) + -> bool +{ + let trait_def = ty::lookup_trait_def(tcx, trait_def_id); + let param_env = ty::construct_parameter_environment(tcx, + &trait_def.generics, + ast::DUMMY_NODE_ID); + let predicates = param_env.caller_bounds.predicates.as_slice().to_vec(); + let sized_def_id = match tcx.lang_items.sized_trait() { + Some(def_id) => def_id, + None => { return false; /* No Sized trait, can't require it! */ } + }; + + // Search for a predicate like `Self : Sized` amongst the trait bounds. + elaborate_predicates(tcx, predicates) + .any(|predicate| { + match predicate { + ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => { + let self_ty = trait_pred.0.self_ty(); + match self_ty.sty { + ty::ty_param(ref data) => data.space == subst::SelfSpace, + _ => false, + } + } + ty::Predicate::Projection(..) | + ty::Predicate::Trait(..) | + ty::Predicate::Equate(..) | + ty::Predicate::RegionOutlives(..) | + ty::Predicate::TypeOutlives(..) => { + false + } + } + }) +} + +fn object_safety_violations_for_method<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_def_id: ast::DefId, + method: &ty::Method<'tcx>) + -> Option +{ + // The method's first parameter must be something that derefs to + // `&self`. For now, we only accept `&self` and `Box`. + match method.explicit_self { + ty::ByValueExplicitSelfCategory => { + return Some(MethodViolationCode::ByValueSelf); + } + + ty::StaticExplicitSelfCategory => { + return Some(MethodViolationCode::StaticMethod); + } + + ty::ByReferenceExplicitSelfCategory(..) | + ty::ByBoxExplicitSelfCategory => { + } + } + + // The `Self` type is erased, so it should not appear in list of + // arguments or return type apart from the receiver. + let ref sig = method.fty.sig; + for &input_ty in sig.0.inputs[1..].iter() { + if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) { + return Some(MethodViolationCode::ReferencesSelf); + } + } + if let ty::FnConverging(result_type) = sig.0.output { + if contains_illegal_self_type_reference(tcx, trait_def_id, result_type) { + return Some(MethodViolationCode::ReferencesSelf); + } + } + + // We can't monomorphize things like `fn foo(...)`. + if !method.generics.types.is_empty_in(subst::FnSpace) { + return Some(MethodViolationCode::Generic); + } + + None +} + +fn contains_illegal_self_type_reference<'tcx>(tcx: &ty::ctxt<'tcx>, + trait_def_id: ast::DefId, + ty: Ty<'tcx>) + -> bool +{ + // This is somewhat subtle. In general, we want to forbid + // references to `Self` in the argument and return types, + // since the value of `Self` is erased. However, there is one + // exception: it is ok to reference `Self` in order to access + // an associated type of the current trait, since we retain + // the value of those associated types in the object type + // itself. + // + // ```rust + // trait SuperTrait { + // type X; + // } + // + // trait Trait : SuperTrait { + // type Y; + // fn foo(&self, x: Self) // bad + // fn foo(&self) -> Self // bad + // fn foo(&self) -> Option // bad + // fn foo(&self) -> Self::Y // OK, desugars to next example + // fn foo(&self) -> ::Y // OK + // fn foo(&self) -> Self::X // OK, desugars to next example + // fn foo(&self) -> ::X // OK + // } + // ``` + // + // However, it is not as simple as allowing `Self` in a projected + // type, because there are illegal ways to use `Self` as well: + // + // ```rust + // trait Trait : SuperTrait { + // ... + // fn foo(&self) -> ::X; + // } + // ``` + // + // Here we will not have the type of `X` recorded in the + // object type, and we cannot resolve `Self as SomeOtherTrait` + // without knowing what `Self` is. + + let mut supertraits: Option>> = None; + let mut error = false; + ty::maybe_walk_ty(ty, |ty| { + match ty.sty { + ty::ty_param(ref param_ty) => { + if param_ty.space == SelfSpace { + error = true; + } + + false // no contained types to walk + } + + ty::ty_projection(ref data) => { + // This is a projected type `::X`. + + // Compute supertraits of current trait lazilly. + if supertraits.is_none() { + let trait_def = ty::lookup_trait_def(tcx, trait_def_id); + let trait_ref = ty::Binder(trait_def.trait_ref.clone()); + supertraits = Some(traits::supertraits(tcx, trait_ref).collect()); + } + + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let projection_trait_ref = ty::Binder(data.trait_ref.clone()); + let is_supertrait_of_current_trait = + supertraits.as_ref().unwrap().contains(&projection_trait_ref); + + if is_supertrait_of_current_trait { + false // do not walk contained types, do not report error, do collect $200 + } else { + true // DO walk contained types, POSSIBLY reporting an error + } + } + + _ => true, // walk contained types, if any + } + }); + + error +} + +impl<'tcx> Repr<'tcx> for ObjectSafetyViolation<'tcx> { + fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { + match *self { + ObjectSafetyViolation::SizedSelf => + format!("SizedSelf"), + ObjectSafetyViolation::Method(ref m, code) => + format!("Method({},{})", m.repr(tcx), code), + } + } +} diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 7bc5d3d0708..89a2f94aa04 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -827,6 +827,9 @@ pub struct ctxt<'tcx> { /// parameters are never placed into this cache, because their /// results are dependent on the parameter environment. pub type_impls_sized_cache: RefCell,bool>>, + + /// Caches whether traits are object safe + pub object_safety_cache: RefCell>, } // Flags that we track on types. These flags are propagated upwards @@ -2384,6 +2387,7 @@ pub fn mk_ctxt<'tcx>(s: Session, repr_hint_cache: RefCell::new(DefIdMap::new()), type_impls_copy_cache: RefCell::new(HashMap::new()), type_impls_sized_cache: RefCell::new(HashMap::new()), + object_safety_cache: RefCell::new(DefIdMap::new()), } } diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index a89292cfacb..a046d9d5d39 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -141,7 +141,7 @@ impl PpSourceMode { } } -trait PrinterSupport<'ast>: pprust::PpAnn + Sized { +trait PrinterSupport<'ast>: pprust::PpAnn { /// Provides a uniform interface for re-extracting a reference to a /// `Session` from a value that now owns it. fn sess<'a>(&'a self) -> &'a Session; @@ -154,7 +154,7 @@ trait PrinterSupport<'ast>: pprust::PpAnn + Sized { /// /// (Rust does not yet support upcasting from a trait object to /// an object for one of its super-traits.) - fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self as &pprust::PpAnn } + fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn; } struct NoAnn<'ast> { @@ -168,6 +168,8 @@ impl<'ast> PrinterSupport<'ast> for NoAnn<'ast> { fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { self.ast_map.as_ref() } + + fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self } } impl<'ast> pprust::PpAnn for NoAnn<'ast> {} @@ -183,6 +185,8 @@ impl<'ast> PrinterSupport<'ast> for IdentifiedAnnotation<'ast> { fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { self.ast_map.as_ref() } + + fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self } } impl<'ast> pprust::PpAnn for IdentifiedAnnotation<'ast> { @@ -232,6 +236,8 @@ impl<'ast> PrinterSupport<'ast> for HygieneAnnotation<'ast> { fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'ast>> { self.ast_map.as_ref() } + + fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self } } impl<'ast> pprust::PpAnn for HygieneAnnotation<'ast> { @@ -265,6 +271,8 @@ impl<'tcx> PrinterSupport<'tcx> for TypedAnnotation<'tcx> { fn ast_map<'a>(&'a self) -> Option<&'a ast_map::Map<'tcx>> { Some(&self.analysis.ty_cx.map) } + + fn pp_ann<'a>(&'a self) -> &'a pprust::PpAnn { self } } impl<'tcx> pprust::PpAnn for TypedAnnotation<'tcx> { diff --git a/src/librustc_typeck/check/vtable.rs b/src/librustc_typeck/check/vtable.rs index c85b542b6ca..65f8ee49908 100644 --- a/src/librustc_typeck/check/vtable.rs +++ b/src/librustc_typeck/check/vtable.rs @@ -10,7 +10,7 @@ use check::{FnCtxt, structurally_resolved_type}; use middle::subst::{FnSpace, SelfSpace}; -use middle::traits; +use middle::traits::{mod, ObjectSafetyViolation, MethodViolationCode}; use middle::traits::{Obligation, ObligationCause}; use middle::traits::report_fulfillment_errors; use middle::ty::{mod, Ty, AsPredicate}; @@ -133,217 +133,56 @@ pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>, object_trait: &ty::TyTrait<'tcx>, span: Span) { - // Also check that the type `object_trait` specifies all - // associated types for all supertraits. - let mut associated_types: FnvHashSet<(ast::DefId, ast::Name)> = FnvHashSet::new(); - let object_trait_ref = object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err); - for tr in traits::supertraits(tcx, object_trait_ref.clone()) { - check_object_safety_inner(tcx, &tr, span); - let trait_def = ty::lookup_trait_def(tcx, object_trait_ref.def_id()); - for &associated_type_name in trait_def.associated_type_names.iter() { - associated_types.insert((object_trait_ref.def_id(), associated_type_name)); - } + if traits::is_object_safe(tcx, object_trait_ref.clone()) { + return; } - for projection_bound in object_trait.bounds.projection_bounds.iter() { - let pair = (projection_bound.0.projection_ty.trait_ref.def_id, - projection_bound.0.projection_ty.item_name); - associated_types.remove(&pair); - } + span_err!(tcx.sess, span, E0038, + "cannot convert to a trait object because trait `{}` is not object-safe", + ty::item_path_str(tcx, object_trait_ref.def_id())); - for (trait_def_id, name) in associated_types.into_iter() { - tcx.sess.span_err( - span, - format!("the value of the associated type `{}` (from the trait `{}`) must be specified", - name.user_string(tcx), - ty::item_path_str(tcx, trait_def_id)).as_slice()); - } -} - -fn check_object_safety_inner<'tcx>(tcx: &ty::ctxt<'tcx>, - object_trait: &ty::PolyTraitRef<'tcx>, - span: Span) { - let trait_items = ty::trait_items(tcx, object_trait.def_id()); - - let mut errors = Vec::new(); - for item in trait_items.iter() { - match *item { - ty::MethodTraitItem(ref m) => { - errors.push(check_object_safety_of_method(tcx, object_trait, &**m)) - } - ty::TypeTraitItem(_) => {} - } - } - - let mut errors = errors.iter().flat_map(|x| x.iter()).peekable(); - if errors.peek().is_some() { - let trait_name = ty::item_path_str(tcx, object_trait.def_id()); - span_err!(tcx.sess, span, E0038, - "cannot convert to a trait object because trait `{}` is not object-safe", - trait_name); - - for msg in errors { - tcx.sess.note(msg[]); - } - } - - /// Returns a vec of error messages. If the vec is empty - no errors! - /// - /// There are some limitations to calling functions through an object, because (a) the self - /// type is not known (that's the whole point of a trait instance, after all, to obscure the - /// self type), (b) the call must go through a vtable and hence cannot be monomorphized and - /// (c) the trait contains static methods which can't be called because we don't know the - /// concrete type. - fn check_object_safety_of_method<'tcx>(tcx: &ty::ctxt<'tcx>, - object_trait: &ty::PolyTraitRef<'tcx>, - method: &ty::Method<'tcx>) - -> Vec { - let mut msgs = Vec::new(); - - let method_name = method.name.repr(tcx); - - match method.explicit_self { - ty::ByValueExplicitSelfCategory => { // reason (a) above - msgs.push(format!("cannot call a method (`{}`) with a by-value \ - receiver through a trait object", method_name)) + let violations = traits::object_safety_violations(tcx, object_trait_ref.clone()); + for violation in violations.into_iter() { + match violation { + ObjectSafetyViolation::SizedSelf => { + tcx.sess.span_note( + span, + "the trait cannot require that `Self : Sized`"); } - ty::StaticExplicitSelfCategory => { - // Static methods are never object safe (reason (c)). - msgs.push(format!("cannot call a static method (`{}`) \ - through a trait object", - method_name)); - return msgs; + ObjectSafetyViolation::Method(method, MethodViolationCode::ByValueSelf) => { + tcx.sess.span_note( + span, + format!("method `{}` has a receiver type of `Self`, \ + which cannot be used with a trait object", + method.name.user_string(tcx)).as_slice()); } - ty::ByReferenceExplicitSelfCategory(..) | - ty::ByBoxExplicitSelfCategory => {} - } - // reason (a) above - let check_for_self_ty = |&: ty| { - if contains_illegal_self_type_reference(tcx, object_trait.def_id(), ty) { - Some(format!( - "cannot call a method (`{}`) whose type contains \ - a self-type (`{}`) through a trait object", - method_name, ty.user_string(tcx))) - } else { - None + ObjectSafetyViolation::Method(method, MethodViolationCode::StaticMethod) => { + tcx.sess.span_note( + span, + format!("method `{}` has no receiver", + method.name.user_string(tcx)).as_slice()); } - }; - let ref sig = method.fty.sig; - for &input_ty in sig.0.inputs[1..].iter() { - if let Some(msg) = check_for_self_ty(input_ty) { - msgs.push(msg); + + ObjectSafetyViolation::Method(method, MethodViolationCode::ReferencesSelf) => { + tcx.sess.span_note( + span, + format!("method `{}` references the `Self` type \ + in its arguments or return type", + method.name.user_string(tcx)).as_slice()); + } + + ObjectSafetyViolation::Method(method, MethodViolationCode::Generic) => { + tcx.sess.span_note( + span, + format!("method `{}` has generic type parameters", + method.name.user_string(tcx)).as_slice()); } } - if let ty::FnConverging(result_type) = sig.0.output { - if let Some(msg) = check_for_self_ty(result_type) { - msgs.push(msg); - } - } - - if method.generics.has_type_params(FnSpace) { - // reason (b) above - msgs.push(format!("cannot call a generic method (`{}`) through a trait object", - method_name)); - } - - msgs - } - - fn contains_illegal_self_type_reference<'tcx>(tcx: &ty::ctxt<'tcx>, - trait_def_id: ast::DefId, - ty: Ty<'tcx>) - -> bool - { - // This is somewhat subtle. In general, we want to forbid - // references to `Self` in the argument and return types, - // since the value of `Self` is erased. However, there is one - // exception: it is ok to reference `Self` in order to access - // an associated type of the current trait, since we retain - // the value of those associated types in the object type - // itself. - // - // ```rust - // trait SuperTrait { - // type X; - // } - // - // trait Trait : SuperTrait { - // type Y; - // fn foo(&self, x: Self) // bad - // fn foo(&self) -> Self // bad - // fn foo(&self) -> Option // bad - // fn foo(&self) -> Self::Y // OK, desugars to next example - // fn foo(&self) -> ::Y // OK - // fn foo(&self) -> Self::X // OK, desugars to next example - // fn foo(&self) -> ::X // OK - // } - // ``` - // - // However, it is not as simple as allowing `Self` in a projected - // type, because there are illegal ways to use `Self` as well: - // - // ```rust - // trait Trait : SuperTrait { - // ... - // fn foo(&self) -> ::X; - // } - // ``` - // - // Here we will not have the type of `X` recorded in the - // object type, and we cannot resolve `Self as SomeOtherTrait` - // without knowing what `Self` is. - - let mut supertraits: Option>> = None; - let mut error = false; - ty::maybe_walk_ty(ty, |ty| { - match ty.sty { - ty::ty_param(ref param_ty) => { - if param_ty.space == SelfSpace { - error = true; - } - - false // no contained types to walk - } - - ty::ty_projection(ref data) => { - // This is a projected type `::X`. - - // Compute supertraits of current trait lazilly. - if supertraits.is_none() { - let trait_def = ty::lookup_trait_def(tcx, trait_def_id); - let trait_ref = ty::Binder(trait_def.trait_ref.clone()); - supertraits = Some(traits::supertraits(tcx, trait_ref).collect()); - } - - // Determine whether the trait reference `Foo as - // SomeTrait` is in fact a supertrait of the - // current trait. In that case, this type is - // legal, because the type `X` will be specified - // in the object type. Note that we can just use - // direct equality here because all of these types - // are part of the formal parameter listing, and - // hence there should be no inference variables. - let projection_trait_ref = ty::Binder(data.trait_ref.clone()); - let is_supertrait_of_current_trait = - supertraits.as_ref().unwrap().contains(&projection_trait_ref); - - if is_supertrait_of_current_trait { - false // do not walk contained types, do not report error, do collect $200 - } else { - true // DO walk contained types, POSSIBLY reporting an error - } - } - - _ => true, // walk contained types, if any - } - }); - - error } } @@ -392,7 +231,7 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, cause.clone()); } - // Finally, create obligations for the projection predicates. + // Create obligations for the projection predicates. let projection_bounds = object_trait.projection_bounds_with_self_ty(fcx.tcx(), referent_ty); for projection_bound in projection_bounds.iter() { @@ -401,9 +240,47 @@ pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, fcx.register_predicate(projection_obligation); } + // Finally, check that there IS a projection predicate for every associated type. + check_object_type_binds_all_associated_types(fcx.tcx(), + span, + object_trait); + object_trait_ref } +fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>, + span: Span, + object_trait: &ty::TyTrait<'tcx>) +{ + let object_trait_ref = + object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err); + + let mut associated_types: FnvHashSet<(ast::DefId, ast::Name)> = + traits::supertraits(tcx, object_trait_ref.clone()) + .flat_map(|tr| { + let trait_def = ty::lookup_trait_def(tcx, tr.def_id()); + trait_def.associated_type_names + .clone() + .into_iter() + .map(move |associated_type_name| (tr.def_id(), associated_type_name)) + }) + .collect(); + + for projection_bound in object_trait.bounds.projection_bounds.iter() { + let pair = (projection_bound.0.projection_ty.trait_ref.def_id, + projection_bound.0.projection_ty.item_name); + associated_types.remove(&pair); + } + + for (trait_def_id, name) in associated_types.into_iter() { + tcx.sess.span_err( + span, + format!("the value of the associated type `{}` (from the trait `{}`) must be specified", + name.user_string(tcx), + ty::item_path_str(tcx, trait_def_id)).as_slice()); + } +} + pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) { debug!("select_all_fcx_obligations_or_error"); diff --git a/src/test/compile-fail/issue-18959.rs b/src/test/compile-fail/issue-18959.rs index 3d126790335..1a792eb6e76 100644 --- a/src/test/compile-fail/issue-18959.rs +++ b/src/test/compile-fail/issue-18959.rs @@ -21,6 +21,6 @@ impl Foo for Thing { fn main() { let mut thing = Thing; - let test: &Bar = &mut thing; //~ ERROR cannot convert to a trait object because trait `Foo` + let test: &Bar = &mut thing; //~ ERROR cannot convert to a trait object foo(test); } diff --git a/src/test/compile-fail/object-safety-by-value-self.rs b/src/test/compile-fail/object-safety-by-value-self.rs new file mode 100644 index 00000000000..5ebcc8516ca --- /dev/null +++ b/src/test/compile-fail/object-safety-by-value-self.rs @@ -0,0 +1,47 @@ +// 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. + +// Check that we correctly prevent users from making trait objects +// from traits with a `fn(self)` method. + +trait Bar { + fn bar(self); +} + +trait Baz { + fn baz(self: Self); +} + +fn make_bar(t: &T) -> &Bar { + t + //~^ ERROR `Bar` is not object-safe + //~| NOTE method `bar` has a receiver type of `Self` +} + +fn make_bar_explicit(t: &T) -> &Bar { + t as &Bar + //~^ ERROR `Bar` is not object-safe + //~| NOTE method `bar` has a receiver type of `Self` +} + +fn make_baz(t: &T) -> &Baz { + t + //~^ ERROR `Baz` is not object-safe + //~| NOTE method `baz` has a receiver type of `Self` +} + +fn make_baz_explicit(t: &T) -> &Baz { + t as &Baz + //~^ ERROR `Baz` is not object-safe + //~| NOTE method `baz` has a receiver type of `Self` +} + +fn main() { +} diff --git a/src/test/run-pass/trait-object-safety.rs b/src/test/compile-fail/object-safety-generics.rs similarity index 51% rename from src/test/run-pass/trait-object-safety.rs rename to src/test/compile-fail/object-safety-generics.rs index ed7284a8353..0ca706404c1 100644 --- a/src/test/run-pass/trait-object-safety.rs +++ b/src/test/compile-fail/object-safety-generics.rs @@ -8,19 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Check that object-safe methods are identified as such. +// Check that we correctly prevent users from making trait objects +// from traits with generic methods. -trait Tr { - fn foo(&self); +trait Bar { + fn bar(&self, t: T); } -struct St; +fn make_bar(t: &T) -> &Bar { + t + //~^ ERROR `Bar` is not object-safe + //~| NOTE method `bar` has generic type parameters +} -impl Tr for St { - fn foo(&self) {} +fn make_bar_explicit(t: &T) -> &Bar { + t as &Bar + //~^ ERROR `Bar` is not object-safe + //~| NOTE method `bar` has generic type parameters } fn main() { - let s: &Tr = &St; - s.foo(); } diff --git a/src/test/compile-fail/object-safety-mentions-Self.rs b/src/test/compile-fail/object-safety-mentions-Self.rs new file mode 100644 index 00000000000..df0f44c1391 --- /dev/null +++ b/src/test/compile-fail/object-safety-mentions-Self.rs @@ -0,0 +1,47 @@ +// 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. + +// Check that we correctly prevent users from making trait objects +// form traits that make use of `Self` in an argument or return position. + +trait Bar { + fn bar(&self, x: &Self); +} + +trait Baz { + fn bar(&self) -> Self; +} + +fn make_bar(t: &T) -> &Bar { + t + //~^ ERROR `Bar` is not object-safe + //~| NOTE method `bar` references the `Self` type in its arguments or return type +} + +fn make_bar_explicit(t: &T) -> &Bar { + t as &Bar + //~^ ERROR `Bar` is not object-safe + //~| NOTE method `bar` references the `Self` type in its arguments or return type +} + +fn make_baz(t: &T) -> &Baz { + t + //~^ ERROR `Baz` is not object-safe + //~| NOTE method `bar` references the `Self` type in its arguments or return type +} + +fn make_baz_explicit(t: &T) -> &Baz { + t as &Baz + //~^ ERROR `Baz` is not object-safe + //~| NOTE method `bar` references the `Self` type in its arguments or return type +} + +fn main() { +} diff --git a/src/test/compile-fail/object-safety-no-static.rs b/src/test/compile-fail/object-safety-no-static.rs new file mode 100644 index 00000000000..6a010d49692 --- /dev/null +++ b/src/test/compile-fail/object-safety-no-static.rs @@ -0,0 +1,31 @@ +// 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. + +// Check that we correctly prevent users from making trait objects +// from traits with static methods. + +trait Foo { + fn foo(); +} + +fn foo_implicit(b: Box) -> Box { + b + //~^ ERROR cannot convert to a trait object + //~| NOTE method `foo` has no receiver +} + +fn foo_explicit(b: Box) -> Box { + b as Box + //~^ ERROR cannot convert to a trait object + //~| NOTE method `foo` has no receiver +} + +fn main() { +} diff --git a/src/test/compile-fail/object-safety-sized-2.rs b/src/test/compile-fail/object-safety-sized-2.rs new file mode 100644 index 00000000000..3a02461bbb2 --- /dev/null +++ b/src/test/compile-fail/object-safety-sized-2.rs @@ -0,0 +1,33 @@ +// 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. + +// Check that we correctly prevent users from making trait objects +// from traits where `Self : Sized`. + +trait Bar + where Self : Sized +{ + fn bar(&self, t: T); +} + +fn make_bar(t: &T) -> &Bar { + t + //~^ ERROR `Bar` is not object-safe + //~| NOTE the trait cannot require that `Self : Sized` +} + +fn make_bar_explicit(t: &T) -> &Bar { + t as &Bar + //~^ ERROR `Bar` is not object-safe + //~| NOTE the trait cannot require that `Self : Sized` +} + +fn main() { +} diff --git a/src/test/compile-fail/object-safety-sized.rs b/src/test/compile-fail/object-safety-sized.rs new file mode 100644 index 00000000000..bc214f6f3d9 --- /dev/null +++ b/src/test/compile-fail/object-safety-sized.rs @@ -0,0 +1,31 @@ +// 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. + +// Check that we correctly prevent users from making trait objects +// from traits where `Self : Sized`. + +trait Bar : Sized { + fn bar(&self, t: T); +} + +fn make_bar(t: &T) -> &Bar { + t + //~^ ERROR `Bar` is not object-safe + //~| NOTE the trait cannot require that `Self : Sized` +} + +fn make_bar_explicit(t: &T) -> &Bar { + t as &Bar + //~^ ERROR `Bar` is not object-safe + //~| NOTE the trait cannot require that `Self : Sized` +} + +fn main() { +} diff --git a/src/test/compile-fail/trait-objects.rs b/src/test/compile-fail/trait-objects.rs deleted file mode 100644 index 88b907a5cb9..00000000000 --- a/src/test/compile-fail/trait-objects.rs +++ /dev/null @@ -1,43 +0,0 @@ -// 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. - -trait Foo { - fn foo(self); -} - -trait Bar { - fn bar(&self, x: &Self); -} - -trait Baz { - fn baz(&self, x: &T); -} - -impl Foo for int { - fn foo(self) {} -} - -impl Bar for int { - fn bar(&self, _x: &int) {} -} - -impl Baz for int { - fn baz(&self, _x: &T) {} -} - -fn main() { - let _: &Foo = &42i; //~ ERROR cannot convert to a trait object - let _: &Bar = &42i; //~ ERROR cannot convert to a trait object - let _: &Baz = &42i; //~ ERROR cannot convert to a trait object - - let _ = &42i as &Foo; //~ ERROR cannot convert to a trait object - let _ = &42i as &Bar; //~ ERROR cannot convert to a trait object - let _ = &42i as &Baz; //~ ERROR cannot convert to a trait object -} diff --git a/src/test/run-pass/issue-7320.rs b/src/test/run-pass/issue-7320.rs deleted file mode 100644 index bd57a3956c7..00000000000 --- a/src/test/run-pass/issue-7320.rs +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - - -trait Foo : Sized { - fn foo(self: Box) { bar(self as Box); } -} - -fn bar(_b: Box) { } - -fn main() {}