about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2014-12-15 21:11:09 -0500
committerNiko Matsakis <niko@alum.mit.edu>2015-01-02 12:08:36 -0500
commit19dcecb2258dcbc6f02be9beac105b57fd43472f (patch)
treec07a4e631f2b9d9ea461cbc11838f8e2f35fe3a6
parent2c1d7a7caa279ae3dc284940eebf08cbac75df1e (diff)
downloadrust-19dcecb2258dcbc6f02be9beac105b57fd43472f.tar.gz
rust-19dcecb2258dcbc6f02be9beac105b57fd43472f.zip
Refactor object-safety into its own (cached) module so that we can
check it more easily; also extend object safety to cover sized types
as well as static methods.  This makes it sufficient so that we can
always ensure that `Foo : Foo` holds for any trait `Foo`.
-rw-r--r--src/librustc/middle/traits/mod.rs5
-rw-r--r--src/librustc/middle/traits/object_safety.rs302
-rw-r--r--src/librustc/middle/ty.rs4
-rw-r--r--src/librustc_driver/pretty.rs12
-rw-r--r--src/librustc_typeck/check/vtable.rs275
-rw-r--r--src/test/compile-fail/issue-18959.rs2
-rw-r--r--src/test/compile-fail/object-safety-by-value-self.rs47
-rw-r--r--src/test/compile-fail/object-safety-generics.rs (renamed from src/test/run-pass/trait-object-safety.rs)21
-rw-r--r--src/test/compile-fail/object-safety-mentions-Self.rs47
-rw-r--r--src/test/compile-fail/object-safety-no-static.rs31
-rw-r--r--src/test/compile-fail/object-safety-sized-2.rs33
-rw-r--r--src/test/compile-fail/object-safety-sized.rs31
-rw-r--r--src/test/compile-fail/trait-objects.rs43
-rw-r--r--src/test/run-pass/issue-7320.rs18
14 files changed, 600 insertions, 271 deletions
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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! "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<ty::Method<'tcx>>, 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<A>(),
+    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<ObjectSafetyViolation<'tcx>>
+{
+    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<ObjectSafetyViolation<'tcx>>
+{
+    // 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<MethodViolationCode>
+{
+    // The method's first parameter must be something that derefs to
+    // `&self`. For now, we only accept `&self` and `Box<Self>`.
+    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<A>(...)`.
+    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<Self> // bad
+    //     fn foo(&self) -> Self::Y // OK, desugars to next example
+    //     fn foo(&self) -> <Self as Trait>::Y // OK
+    //     fn foo(&self) -> Self::X // OK, desugars to next example
+    //     fn foo(&self) -> <Self as SuperTrait>::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) -> <Self as SomeOtherTrait>::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<Vec<ty::PolyTraitRef<'tcx>>> = 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 `<Foo as SomeTrait>::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<HashMap<Ty<'tcx>,bool>>,
+
+    /// Caches whether traits are object safe
+    pub object_safety_cache: RefCell<DefIdMap<bool>>,
 }
 
 // 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));
-        }
-    }
 
-    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);
+    if traits::is_object_safe(tcx, object_trait_ref.clone()) {
+        return;
     }
 
-    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))
+    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()));
+
+    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::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<String> {
-        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))
+            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::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::StaticMethod) => {
+                tcx.sess.span_note(
+                    span,
+                    format!("method `{}` has no receiver",
+                            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
-            }
-        };
-        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);
-            }
-        }
-        if let ty::FnConverging(result_type) = sig.0.output {
-            if let Some(msg) = check_for_self_ty(result_type) {
-                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());
             }
-        }
-
-        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<Self> // bad
-        //     fn foo(&self) -> Self::Y // OK, desugars to next example
-        //     fn foo(&self) -> <Self as Trait>::Y // OK
-        //     fn foo(&self) -> Self::X // OK, desugars to next example
-        //     fn foo(&self) -> <Self as SuperTrait>::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) -> <Self as SomeOtherTrait>::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<Vec<ty::PolyTraitRef<'tcx>>> = 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 `<Foo as SomeTrait>::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
+            ObjectSafetyViolation::Method(method, MethodViolationCode::Generic) => {
+                tcx.sess.span_note(
+                    span,
+                    format!("method `{}` has generic type parameters",
+                            method.name.user_string(tcx)).as_slice());
             }
-        });
-
-        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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// 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: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:Bar>(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: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:Baz>(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
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<T>(&self, t: T);
 }
 
-struct St;
+fn make_bar<T: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:Bar>(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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// 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: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:Bar>(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: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:Baz>(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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that we correctly prevent users from making trait objects
+// from traits with static methods.
+
+trait Foo {
+    fn foo();
+}
+
+fn foo_implicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
+    b
+        //~^ ERROR cannot convert to a trait object
+        //~| NOTE method `foo` has no receiver
+}
+
+fn foo_explicit<T:Foo+'static>(b: Box<T>) -> Box<Foo+'static> {
+    b as Box<Foo>
+        //~^ 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that we correctly prevent users from making trait objects
+// from traits where `Self : Sized`.
+
+trait Bar
+    where Self : Sized
+{
+    fn bar<T>(&self, t: T);
+}
+
+fn make_bar<T:Bar>(t: &T) -> &Bar {
+    t
+        //~^ ERROR `Bar` is not object-safe
+        //~| NOTE the trait cannot require that `Self : Sized`
+}
+
+fn make_bar_explicit<T:Bar>(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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that we correctly prevent users from making trait objects
+// from traits where `Self : Sized`.
+
+trait Bar : Sized {
+    fn bar<T>(&self, t: T);
+}
+
+fn make_bar<T:Bar>(t: &T) -> &Bar {
+    t
+        //~^ ERROR `Bar` is not object-safe
+        //~| NOTE the trait cannot require that `Self : Sized`
+}
+
+fn make_bar_explicit<T:Bar>(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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-trait Foo {
-    fn foo(self);
-}
-
-trait Bar {
-    fn bar(&self, x: &Self);
-}
-
-trait Baz {
-    fn baz<T>(&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<T>(&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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-trait Foo : Sized {
-    fn foo(self: Box<Self>) { bar(self as Box<Foo>); }
-}
-
-fn bar(_b: Box<Foo>) { }
-
-fn main() {}