about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-03-24 15:27:14 -0700
committerAlex Crichton <alex@alexcrichton.com>2015-03-24 15:27:14 -0700
commit3b13b9c2b4e72d08cb1c68024ccc4f50001f4878 (patch)
treed4efd6426beeeee1f0c543cfe345b9625285f46e
parent91b633aa038008fdbee658a10182afdd794d2aa6 (diff)
parent1955e052675d4457432da85a00db0ae55be64e83 (diff)
downloadrust-3b13b9c2b4e72d08cb1c68024ccc4f50001f4878.tar.gz
rust-3b13b9c2b4e72d08cb1c68024ccc4f50001f4878.zip
rollup merge of #23638: pnkfelix/fsk-reject-specialized-drops
Reject specialized Drop impls.

See Issue #8142 for discussion.

This makes it illegal for a Drop impl to be more specialized than the original item.

So for example, all of the following are now rejected (when they would have been blindly accepted before):

```rust
struct S<A> { ... };
impl Drop for S<i8> { ... } // error: specialized to concrete type

struct T<'a> { ... };
impl Drop for T<'static> { ... } // error: specialized to concrete region

struct U<A> { ... };
impl<A:Clone> Drop for U<A> { ... } // error: added extra type requirement

struct V<'a,'b>;
impl<'a,'b:a> Drop for V<'a,'b> { ... } // error: added extra region requirement
```

Due to examples like the above, this is a [breaking-change].

(The fix is to either remove the specialization from the `Drop` impl, or to transcribe the requirements into the struct/enum definition; examples of both are shown in the PR's fixed to `libstd`.)

----

This is likely to be the last thing blocking the removal of the `#[unsafe_destructor]` attribute.

Fix #8142
Fix #23584
-rw-r--r--src/doc/trpl/unsafe.md17
-rw-r--r--src/liballoc/arc.rs6
-rw-r--r--src/librustc/middle/infer/higher_ranked/mod.rs58
-rw-r--r--src/librustc/middle/infer/mod.rs9
-rw-r--r--src/librustc/middle/ty.rs3
-rw-r--r--src/librustc_typeck/check/dropck.rs240
-rw-r--r--src/librustc_typeck/check/mod.rs14
-rw-r--r--src/librustc_typeck/diagnostics.rs4
-rw-r--r--src/libstd/io/buffered.rs18
-rw-r--r--src/libstd/old_io/buffered.rs18
-rw-r--r--src/libstd/sync/mpsc/mod.rs16
-rw-r--r--src/libstd/sync/mpsc/mpsc_queue.rs2
-rw-r--r--src/libstd/sync/mpsc/oneshot.rs8
-rw-r--r--src/libstd/sync/mpsc/select.rs2
-rw-r--r--src/libstd/sync/mpsc/shared.rs2
-rw-r--r--src/libstd/sync/mpsc/spsc_queue.rs2
-rw-r--r--src/libstd/sync/mpsc/stream.rs8
-rw-r--r--src/libstd/sync/mpsc/sync.rs2
-rw-r--r--src/libstd/sync/mutex.rs4
-rw-r--r--src/libstd/sys/common/helper_thread.rs2
-rw-r--r--src/libstd/thread/mod.rs2
-rw-r--r--src/test/auxiliary/issue-2526.rs2
-rw-r--r--src/test/compile-fail/reject-specialized-drops-8142.rs79
-rw-r--r--src/test/run-pass/issue-15858.rs2
-rw-r--r--src/test/run-pass/issue-15924.rs2
-rw-r--r--src/test/run-pass/issue-2718.rs4
-rw-r--r--src/test/run-pass/issue-4252.rs2
27 files changed, 465 insertions, 63 deletions
diff --git a/src/doc/trpl/unsafe.md b/src/doc/trpl/unsafe.md
index 2116976d55a..dbf0cae6f4b 100644
--- a/src/doc/trpl/unsafe.md
+++ b/src/doc/trpl/unsafe.md
@@ -197,15 +197,16 @@ use std::ptr;
 
 // Define a wrapper around the handle returned by the foreign code.
 // Unique<T> has the same semantics as Box<T>
-pub struct Unique<T> {
+//
+// NB: For simplicity and correctness, we require that T has kind Send
+// (owned boxes relax this restriction).
+pub struct Unique<T: Send> {
     // It contains a single raw, mutable pointer to the object in question.
     ptr: *mut T
 }
 
 // Implement methods for creating and using the values in the box.
 
-// NB: For simplicity and correctness, we require that T has kind Send
-// (owned boxes relax this restriction).
 impl<T: Send> Unique<T> {
     pub fn new(value: T) -> Unique<T> {
         unsafe {
@@ -239,11 +240,11 @@ impl<T: Send> Unique<T> {
 // Unique<T>, making the struct manage the raw pointer: when the
 // struct goes out of scope, it will automatically free the raw pointer.
 //
-// NB: This is an unsafe destructor, because rustc will not normally
-// allow destructors to be associated with parameterized types, due to
-// bad interaction with managed boxes. (With the Send restriction,
-// we don't have this problem.) Note that the `#[unsafe_destructor]`
-// feature gate is required to use unsafe destructors.
+// NB: This is an unsafe destructor; rustc will not normally allow
+// destructors to be associated with parameterized types (due to
+// historically failing to check them soundly).  Note that the
+// `#[unsafe_destructor]` feature gate is currently required to use
+// unsafe destructors.
 #[unsafe_destructor]
 impl<T: Send> Drop for Unique<T> {
     fn drop(&mut self) {
diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs
index c9bbc0d74cd..b5d16d29272 100644
--- a/src/liballoc/arc.rs
+++ b/src/liballoc/arc.rs
@@ -321,7 +321,7 @@ impl<T: Send + Sync + Clone> Arc<T> {
 
 #[unsafe_destructor]
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T: Sync + Send> Drop for Arc<T> {
+impl<T> Drop for Arc<T> {
     /// Drops the `Arc<T>`.
     ///
     /// This will decrement the strong reference count. If the strong reference
@@ -388,7 +388,7 @@ impl<T: Sync + Send> Drop for Arc<T> {
 
 #[unstable(feature = "alloc",
            reason = "Weak pointers may not belong in this module.")]
-impl<T: Sync + Send> Weak<T> {
+impl<T> Weak<T> {
     /// Upgrades a weak reference to a strong reference.
     ///
     /// Upgrades the `Weak<T>` reference to an `Arc<T>`, if possible.
@@ -454,7 +454,7 @@ impl<T: Sync + Send> Clone for Weak<T> {
 
 #[unsafe_destructor]
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T: Sync + Send> Drop for Weak<T> {
+impl<T> Drop for Weak<T> {
     /// Drops the `Weak<T>`.
     ///
     /// This will decrement the weak reference count.
diff --git a/src/librustc/middle/infer/higher_ranked/mod.rs b/src/librustc/middle/infer/higher_ranked/mod.rs
index 7d789bedc50..16b387330b9 100644
--- a/src/librustc/middle/infer/higher_ranked/mod.rs
+++ b/src/librustc/middle/infer/higher_ranked/mod.rs
@@ -14,6 +14,7 @@
 use super::{CombinedSnapshot, cres, InferCtxt, HigherRankedType, SkolemizationMap};
 use super::combine::{Combine, Combineable};
 
+use middle::subst;
 use middle::ty::{self, Binder};
 use middle::ty_fold::{self, TypeFoldable};
 use syntax::codemap::Span;
@@ -455,6 +456,63 @@ impl<'a,'tcx> InferCtxtExt for InferCtxt<'a,'tcx> {
     }
 }
 
+/// Constructs and returns a substitution that, for a given type
+/// scheme parameterized by `generics`, will replace every generic
+/// parmeter in the type with a skolemized type/region (which one can
+/// think of as a "fresh constant", except at the type/region level of
+/// reasoning).
+///
+/// Since we currently represent bound/free type parameters in the
+/// same way, this only has an effect on regions.
+///
+/// (Note that unlike a substitution from `ty::construct_free_substs`,
+/// this inserts skolemized regions rather than free regions; this
+/// allows one to use `fn leak_check` to catch attmepts to unify the
+/// skolemized regions with e.g. the `'static` lifetime)
+pub fn construct_skolemized_substs<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                                            generics: &ty::Generics<'tcx>,
+                                            snapshot: &CombinedSnapshot)
+                                            -> (subst::Substs<'tcx>, SkolemizationMap)
+{
+    let mut map = FnvHashMap();
+
+    // map T => T
+    let mut types = subst::VecPerParamSpace::empty();
+    push_types_from_defs(infcx.tcx, &mut types, generics.types.as_slice());
+
+    // map early- or late-bound 'a => fresh 'a
+    let mut regions = subst::VecPerParamSpace::empty();
+    push_region_params(infcx, &mut map, &mut regions, generics.regions.as_slice(), snapshot);
+
+    let substs = subst::Substs { types: types,
+                                 regions: subst::NonerasedRegions(regions) };
+    return (substs, map);
+
+    fn push_region_params<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                                   map: &mut SkolemizationMap,
+                                   regions: &mut subst::VecPerParamSpace<ty::Region>,
+                                   region_params: &[ty::RegionParameterDef],
+                                   snapshot: &CombinedSnapshot)
+    {
+        for r in region_params {
+            let br = r.to_bound_region();
+            let skol_var = infcx.region_vars.new_skolemized(br, &snapshot.region_vars_snapshot);
+            let sanity_check = map.insert(br, skol_var);
+            assert!(sanity_check.is_none());
+            regions.push(r.space, skol_var);
+        }
+    }
+
+    fn push_types_from_defs<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                  types: &mut subst::VecPerParamSpace<ty::Ty<'tcx>>,
+                                  defs: &[ty::TypeParameterDef<'tcx>]) {
+        for def in defs {
+            let ty = ty::mk_param_from_def(tcx, def);
+            types.push(def.space, ty);
+        }
+    }
+}
+
 pub fn skolemize_late_bound_regions<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
                                                binder: &ty::Binder<T>,
                                                snapshot: &CombinedSnapshot)
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index 835964828d4..a38adabee91 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -726,6 +726,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         })
     }
 
+    pub fn construct_skolemized_subst(&self,
+                                      generics: &ty::Generics<'tcx>,
+                                      snapshot: &CombinedSnapshot)
+                                      -> (subst::Substs<'tcx>, SkolemizationMap) {
+        /*! See `higher_ranked::construct_skolemized_subst` */
+
+        higher_ranked::construct_skolemized_substs(self, generics, snapshot)
+    }
+
     pub fn skolemize_late_bound_regions<T>(&self,
                                            value: &ty::Binder<T>,
                                            snapshot: &CombinedSnapshot)
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 7eb54ca9daf..92b444e85d8 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -1793,6 +1793,9 @@ impl RegionParameterDef {
     pub fn to_early_bound_region(&self) -> ty::Region {
         ty::ReEarlyBound(self.def_id.node, self.space, self.index, self.name)
     }
+    pub fn to_bound_region(&self) -> ty::BoundRegion {
+        ty::BoundRegion::BrNamed(self.def_id, self.name)
+    }
 }
 
 /// Information about the formal type/lifetime parameters associated
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 9c48ac43ee4..c48033cab89 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -12,13 +12,249 @@ use check::regionck::{self, Rcx};
 
 use middle::infer;
 use middle::region;
-use middle::subst;
+use middle::subst::{self, Subst};
 use middle::ty::{self, Ty};
 use util::ppaux::{Repr, UserString};
 
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax::codemap::{self, Span};
+
+/// check_drop_impl confirms that the Drop implementation identfied by
+/// `drop_impl_did` is not any more specialized than the type it is
+/// attached to (Issue #8142).
+///
+/// This means:
+///
+/// 1. The self type must be nominal (this is already checked during
+///    coherence),
+///
+/// 2. The generic region/type parameters of the impl's self-type must
+///    all be parameters of the Drop impl itself (i.e. no
+///    specialization like `impl Drop for Foo<i32>`), and,
+///
+/// 3. Any bounds on the generic parameters must be reflected in the
+///    struct/enum definition for the nominal type itself (i.e.
+///    cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
+///
+pub fn check_drop_impl(tcx: &ty::ctxt, drop_impl_did: ast::DefId) -> Result<(), ()> {
+    let ty::TypeScheme { generics: ref dtor_generics,
+                         ty: ref dtor_self_type } = ty::lookup_item_type(tcx, drop_impl_did);
+    let dtor_predicates = ty::lookup_predicates(tcx, drop_impl_did);
+    match dtor_self_type.sty {
+        ty::ty_enum(self_type_did, self_to_impl_substs) |
+        ty::ty_struct(self_type_did, self_to_impl_substs) |
+        ty::ty_closure(self_type_did, self_to_impl_substs) => {
+            try!(ensure_drop_params_and_item_params_correspond(tcx,
+                                                               drop_impl_did,
+                                                               dtor_generics,
+                                                               dtor_self_type,
+                                                               self_type_did));
+
+            ensure_drop_predicates_are_implied_by_item_defn(tcx,
+                                                            drop_impl_did,
+                                                            &dtor_predicates,
+                                                            self_type_did,
+                                                            self_to_impl_substs)
+        }
+        _ => {
+            // Destructors only work on nominal types.  This was
+            // already checked by coherence, so we can panic here.
+            let span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
+            tcx.sess.span_bug(
+                span, &format!("should have been rejected by coherence check: {}",
+                               dtor_self_type.repr(tcx)));
+        }
+    }
+}
+
+fn ensure_drop_params_and_item_params_correspond<'tcx>(
+    tcx: &ty::ctxt<'tcx>,
+    drop_impl_did: ast::DefId,
+    drop_impl_generics: &ty::Generics<'tcx>,
+    drop_impl_ty: &ty::Ty<'tcx>,
+    self_type_did: ast::DefId) -> Result<(), ()>
+{
+    // New strategy based on review suggestion from nikomatsakis.
+    //
+    // (In the text and code below, "named" denotes "struct/enum", and
+    // "generic params" denotes "type and region params")
+    //
+    // 1. Create fresh skolemized type/region "constants" for each of
+    //    the named type's generic params.  Instantiate the named type
+    //    with the fresh constants, yielding `named_skolem`.
+    //
+    // 2. Create unification variables for each of the Drop impl's
+    //    generic params.  Instantiate the impl's Self's type with the
+    //    unification-vars, yielding `drop_unifier`.
+    //
+    // 3. Attempt to unify Self_unif with Type_skolem.  If unification
+    //    succeeds, continue (i.e. with the predicate checks).
+
+    let ty::TypeScheme { generics: ref named_type_generics,
+                         ty: named_type } =
+        ty::lookup_item_type(tcx, self_type_did);
+
+    let infcx = infer::new_infer_ctxt(tcx);
+    infcx.try(|snapshot| {
+        let (named_type_to_skolem, skol_map) =
+            infcx.construct_skolemized_subst(named_type_generics, snapshot);
+        let named_type_skolem = named_type.subst(tcx, &named_type_to_skolem);
+
+        let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
+        let drop_to_unifier =
+            infcx.fresh_substs_for_generics(drop_impl_span, drop_impl_generics);
+        let drop_unifier = drop_impl_ty.subst(tcx, &drop_to_unifier);
+
+        if let Ok(()) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span),
+                                       named_type_skolem, drop_unifier) {
+            // Even if we did manage to equate the types, the process
+            // may have just gathered unsolvable region constraints
+            // like `R == 'static` (represented as a pair of subregion
+            // constraints) for some skolemization constant R.
+            //
+            // However, the leak_check method allows us to confirm
+            // that no skolemized regions escaped (i.e. were related
+            // to other regions in the constraint graph).
+            if let Ok(()) = infcx.leak_check(&skol_map, snapshot) {
+                return Ok(())
+            }
+        }
+
+        span_err!(tcx.sess, drop_impl_span, E0366,
+                  "Implementations of Drop cannot be specialized");
+        let item_span = tcx.map.span(self_type_did.node);
+        tcx.sess.span_note(item_span,
+                           "Use same sequence of generic type and region \
+                            parameters that is on the struct/enum definition");
+        return Err(());
+    })
+}
+
+/// Confirms that every predicate imposed by dtor_predicates is
+/// implied by assuming the predicates attached to self_type_did.
+fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
+    tcx: &ty::ctxt<'tcx>,
+    drop_impl_did: ast::DefId,
+    dtor_predicates: &ty::GenericPredicates<'tcx>,
+    self_type_did: ast::DefId,
+    self_to_impl_substs: &subst::Substs<'tcx>) -> Result<(), ()> {
+
+    // Here is an example, analogous to that from
+    // `compare_impl_method`.
+    //
+    // Consider a struct type:
+    //
+    //     struct Type<'c, 'b:'c, 'a> {
+    //         x: &'a Contents            // (contents are irrelevant;
+    //         y: &'c Cell<&'b Contents>, //  only the bounds matter for our purposes.)
+    //     }
+    //
+    // and a Drop impl:
+    //
+    //     impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> {
+    //         fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y)
+    //     }
+    //
+    // We start out with self_to_impl_substs, that maps the generic
+    // parameters of Type to that of the Drop impl.
+    //
+    //     self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x}
+    //
+    // Applying this to the predicates (i.e. assumptions) provided by the item
+    // definition yields the instantiated assumptions:
+    //
+    //     ['y : 'z]
+    //
+    // We then check all of the predicates of the Drop impl:
+    //
+    //     ['y:'z, 'x:'y]
+    //
+    // and ensure each is in the list of instantiated
+    // assumptions. Here, `'y:'z` is present, but `'x:'y` is
+    // absent. So we report an error that the Drop impl injected a
+    // predicate that is not present on the struct definition.
+
+    assert_eq!(self_type_did.krate, ast::LOCAL_CRATE);
+
+    let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
+
+    // We can assume the predicates attached to struct/enum definition
+    // hold.
+    let generic_assumptions = ty::lookup_predicates(tcx, self_type_did);
+
+    let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
+    assert!(assumptions_in_impl_context.predicates.is_empty_in(subst::SelfSpace));
+    assert!(assumptions_in_impl_context.predicates.is_empty_in(subst::FnSpace));
+    let assumptions_in_impl_context =
+        assumptions_in_impl_context.predicates.get_slice(subst::TypeSpace);
+
+    // An earlier version of this code attempted to do this checking
+    // via the traits::fulfill machinery. However, it ran into trouble
+    // since the fulfill machinery merely turns outlives-predicates
+    // 'a:'b and T:'b into region inference constraints. It is simpler
+    // just to look for all the predicates directly.
+
+    assert!(dtor_predicates.predicates.is_empty_in(subst::SelfSpace));
+    assert!(dtor_predicates.predicates.is_empty_in(subst::FnSpace));
+    let predicates = dtor_predicates.predicates.get_slice(subst::TypeSpace);
+    for predicate in predicates {
+        // (We do not need to worry about deep analysis of type
+        // expressions etc because the Drop impls are already forced
+        // to take on a structure that is roughly a alpha-renaming of
+        // the generic parameters of the item definition.)
+
+        // This path now just checks *all* predicates via the direct
+        // lookup, rather than using fulfill machinery.
+        //
+        // However, it may be more efficient in the future to batch
+        // the analysis together via the fulfill , rather than the
+        // repeated `contains` calls.
+
+        if !assumptions_in_impl_context.contains(&predicate) {
+            let item_span = tcx.map.span(self_type_did.node);
+            let req = predicate.user_string(tcx);
+            span_err!(tcx.sess, drop_impl_span, E0367,
+                      "The requirement `{}` is added only by the Drop impl.", req);
+            tcx.sess.span_note(item_span,
+                               "The same requirement must be part of \
+                                the struct/enum definition");
+        }
+    }
+
+    if tcx.sess.has_errors() {
+        return Err(());
+    }
+    Ok(())
+}
 
+/// check_safety_of_destructor_if_necessary confirms that the type
+/// expression `typ` conforms to the "Drop Check Rule" from the Sound
+/// Generic Drop (RFC 769).
+///
+/// ----
+///
+/// The Drop Check Rule is the following:
+///
+/// Let `v` be some value (either temporary or named) and 'a be some
+/// lifetime (scope). If the type of `v` owns data of type `D`, where
+///
+///   (1.) `D` has a lifetime- or type-parametric Drop implementation, and
+///   (2.) the structure of `D` can reach a reference of type `&'a _`, and
+///   (3.) either:
+///
+///     (A.) the Drop impl for `D` instantiates `D` at 'a directly,
+///          i.e. `D<'a>`, or,
+///
+///     (B.) the Drop impl for `D` has some type parameter with a
+///          trait bound `T` where `T` is a trait that has at least
+///          one method,
+///
+/// then 'a must strictly outlive the scope of v.
+///
+/// ----
+///
+/// This function is meant to by applied to the type for every
+/// expression in the program.
 pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
                                                      typ: ty::Ty<'tcx>,
                                                      span: Span,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 47aafa2251d..1e38a7d2d9f 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -490,6 +490,20 @@ pub fn check_item_types(ccx: &CrateCtxt) {
     visit::walk_crate(&mut visit, krate);
 
     ccx.tcx.sess.abort_if_errors();
+
+    for drop_method_did in ccx.tcx.destructors.borrow().iter() {
+        if drop_method_did.krate == ast::LOCAL_CRATE {
+            let drop_impl_did = ccx.tcx.map.get_parent_did(drop_method_did.node);
+            match dropck::check_drop_impl(ccx.tcx, drop_impl_did) {
+                Ok(()) => {}
+                Err(()) => {
+                    assert!(ccx.tcx.sess.has_errors());
+                }
+            }
+        }
+    }
+
+    ccx.tcx.sess.abort_if_errors();
 }
 
 fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 396d060de9e..95e06879fb2 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -177,7 +177,9 @@ register_diagnostics! {
     E0319, // trait impls for defaulted traits allowed just for structs/enums
     E0320, // recursive overflow during dropck
     E0321, // extended coherence rules for defaulted traits violated
-    E0322  // cannot implement Sized explicitly
+    E0322, // cannot implement Sized explicitly
+    E0366, // dropck forbid specialization to concrete type or region
+    E0367  // dropck forbid specialization to predicate not in struct/enum
 }
 
 __build_diagnostic_array! { DIAGNOSTICS }
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index 4def601f1c0..2a1294f23b2 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -120,7 +120,7 @@ impl<R> fmt::Debug for BufReader<R> where R: fmt::Debug {
 ///
 /// The buffer will be written out when the writer is dropped.
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct BufWriter<W> {
+pub struct BufWriter<W: Write> {
     inner: Option<W>,
     buf: Vec<u8>,
 }
@@ -220,7 +220,7 @@ impl<W: Write> Write for BufWriter<W> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<W> fmt::Debug for BufWriter<W> where W: fmt::Debug {
+impl<W: Write> fmt::Debug for BufWriter<W> where W: fmt::Debug {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         write!(fmt, "BufWriter {{ writer: {:?}, buffer: {}/{} }}",
                self.inner.as_ref().unwrap(), self.buf.len(), self.buf.capacity())
@@ -276,7 +276,7 @@ impl<W> fmt::Display for IntoInnerError<W> {
 ///
 /// The buffer will be written out when the writer is dropped.
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct LineWriter<W> {
+pub struct LineWriter<W: Write> {
     inner: BufWriter<W>,
 }
 
@@ -335,7 +335,7 @@ impl<W: Write> Write for LineWriter<W> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<W> fmt::Debug for LineWriter<W> where W: fmt::Debug {
+impl<W: Write> fmt::Debug for LineWriter<W> where W: fmt::Debug {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         write!(fmt, "LineWriter {{ writer: {:?}, buffer: {}/{} }}",
                self.inner.inner, self.inner.buf.len(),
@@ -343,16 +343,16 @@ impl<W> fmt::Debug for LineWriter<W> where W: fmt::Debug {
     }
 }
 
-struct InternalBufWriter<W>(BufWriter<W>);
+struct InternalBufWriter<W: Write>(BufWriter<W>);
 
-impl<W> InternalBufWriter<W> {
+impl<W: Read + Write> InternalBufWriter<W> {
     fn get_mut(&mut self) -> &mut BufWriter<W> {
         let InternalBufWriter(ref mut w) = *self;
         return w;
     }
 }
 
-impl<W: Read> Read for InternalBufWriter<W> {
+impl<W: Read + Write> Read for InternalBufWriter<W> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.get_mut().inner.as_mut().unwrap().read(buf)
     }
@@ -367,7 +367,7 @@ impl<W: Read> Read for InternalBufWriter<W> {
 ///
 /// The output buffer will be written out when this stream is dropped.
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct BufStream<S> {
+pub struct BufStream<S: Write> {
     inner: BufReader<InternalBufWriter<S>>
 }
 
@@ -448,7 +448,7 @@ impl<S: Read + Write> Write for BufStream<S> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<S> fmt::Debug for BufStream<S> where S: fmt::Debug {
+impl<S: Write> fmt::Debug for BufStream<S> where S: fmt::Debug {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         let reader = &self.inner;
         let writer = &self.inner.inner.0;
diff --git a/src/libstd/old_io/buffered.rs b/src/libstd/old_io/buffered.rs
index cb67d709a14..9a9d421dfe1 100644
--- a/src/libstd/old_io/buffered.rs
+++ b/src/libstd/old_io/buffered.rs
@@ -148,14 +148,14 @@ impl<R: Reader> Reader for BufferedReader<R> {
 /// writer.write_str("hello, world").unwrap();
 /// writer.flush().unwrap();
 /// ```
-pub struct BufferedWriter<W> {
+pub struct BufferedWriter<W: Writer> {
     inner: Option<W>,
     buf: Vec<u8>,
     pos: uint
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<W> fmt::Debug for BufferedWriter<W> where W: fmt::Debug {
+impl<W: Writer> fmt::Debug for BufferedWriter<W> where W: fmt::Debug {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         write!(fmt, "BufferedWriter {{ writer: {:?}, buffer: {}/{} }}",
                self.inner.as_ref().unwrap(), self.pos, self.buf.len())
@@ -250,12 +250,12 @@ impl<W: Writer> Drop for BufferedWriter<W> {
 /// `'\n'`) is detected.
 ///
 /// This writer will be flushed when it is dropped.
-pub struct LineBufferedWriter<W> {
+pub struct LineBufferedWriter<W: Writer> {
     inner: BufferedWriter<W>,
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<W> fmt::Debug for LineBufferedWriter<W> where W: fmt::Debug {
+impl<W: Writer> fmt::Debug for LineBufferedWriter<W> where W: fmt::Debug {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         write!(fmt, "LineBufferedWriter {{ writer: {:?}, buffer: {}/{} }}",
                self.inner.inner, self.inner.pos, self.inner.buf.len())
@@ -299,16 +299,16 @@ impl<W: Writer> Writer for LineBufferedWriter<W> {
     fn flush(&mut self) -> IoResult<()> { self.inner.flush() }
 }
 
-struct InternalBufferedWriter<W>(BufferedWriter<W>);
+struct InternalBufferedWriter<W: Writer>(BufferedWriter<W>);
 
-impl<W> InternalBufferedWriter<W> {
+impl<W: Writer> InternalBufferedWriter<W> {
     fn get_mut<'a>(&'a mut self) -> &'a mut BufferedWriter<W> {
         let InternalBufferedWriter(ref mut w) = *self;
         return w;
     }
 }
 
-impl<W: Reader> Reader for InternalBufferedWriter<W> {
+impl<W: Reader + Writer> Reader for InternalBufferedWriter<W> {
     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
         self.get_mut().inner.as_mut().unwrap().read(buf)
     }
@@ -343,12 +343,12 @@ impl<W: Reader> Reader for InternalBufferedWriter<W> {
 ///     Err(e) => println!("error reading: {}", e)
 /// }
 /// ```
-pub struct BufferedStream<S> {
+pub struct BufferedStream<S: Writer> {
     inner: BufferedReader<InternalBufferedWriter<S>>
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<S> fmt::Debug for BufferedStream<S> where S: fmt::Debug {
+impl<S: Writer> fmt::Debug for BufferedStream<S> where S: fmt::Debug {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         let reader = &self.inner;
         let writer = &self.inner.inner.0;
diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs
index 7adfd9154ac..eb421fe55a4 100644
--- a/src/libstd/sync/mpsc/mod.rs
+++ b/src/libstd/sync/mpsc/mod.rs
@@ -342,7 +342,7 @@ mod spsc_queue;
 /// The receiving-half of Rust's channel type. This half can only be owned by
 /// one task
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct Receiver<T> {
+pub struct Receiver<T:Send> {
     inner: UnsafeCell<Flavor<T>>,
 }
 
@@ -354,14 +354,14 @@ unsafe impl<T: Send> Send for Receiver<T> { }
 /// whenever `next` is called, waiting for a new message, and `None` will be
 /// returned when the corresponding channel has hung up.
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct Iter<'a, T:'a> {
+pub struct Iter<'a, T:Send+'a> {
     rx: &'a Receiver<T>
 }
 
 /// The sending-half of Rust's asynchronous channel type. This half can only be
 /// owned by one task, but it can be cloned to send to other tasks.
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct Sender<T> {
+pub struct Sender<T:Send> {
     inner: UnsafeCell<Flavor<T>>,
 }
 
@@ -372,7 +372,7 @@ unsafe impl<T: Send> Send for Sender<T> { }
 /// The sending-half of Rust's synchronous channel type. This half can only be
 /// owned by one task, but it can be cloned to send to other tasks.
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct SyncSender<T> {
+pub struct SyncSender<T: Send> {
     inner: Arc<UnsafeCell<sync::Packet<T>>>,
 }
 
@@ -433,7 +433,7 @@ pub enum TrySendError<T> {
     Disconnected(T),
 }
 
-enum Flavor<T> {
+enum Flavor<T:Send> {
     Oneshot(Arc<UnsafeCell<oneshot::Packet<T>>>),
     Stream(Arc<UnsafeCell<stream::Packet<T>>>),
     Shared(Arc<UnsafeCell<shared::Packet<T>>>),
@@ -441,7 +441,7 @@ enum Flavor<T> {
 }
 
 #[doc(hidden)]
-trait UnsafeFlavor<T> {
+trait UnsafeFlavor<T:Send> {
     fn inner_unsafe<'a>(&'a self) -> &'a UnsafeCell<Flavor<T>>;
     unsafe fn inner_mut<'a>(&'a self) -> &'a mut Flavor<T> {
         &mut *self.inner_unsafe().get()
@@ -450,12 +450,12 @@ trait UnsafeFlavor<T> {
         &*self.inner_unsafe().get()
     }
 }
-impl<T> UnsafeFlavor<T> for Sender<T> {
+impl<T:Send> UnsafeFlavor<T> for Sender<T> {
     fn inner_unsafe<'a>(&'a self) -> &'a UnsafeCell<Flavor<T>> {
         &self.inner
     }
 }
-impl<T> UnsafeFlavor<T> for Receiver<T> {
+impl<T:Send> UnsafeFlavor<T> for Receiver<T> {
     fn inner_unsafe<'a>(&'a self) -> &'a UnsafeCell<Flavor<T>> {
         &self.inner
     }
diff --git a/src/libstd/sync/mpsc/mpsc_queue.rs b/src/libstd/sync/mpsc/mpsc_queue.rs
index 14ed253d8e2..1be8b0dd862 100644
--- a/src/libstd/sync/mpsc/mpsc_queue.rs
+++ b/src/libstd/sync/mpsc/mpsc_queue.rs
@@ -72,7 +72,7 @@ struct Node<T> {
 /// The multi-producer single-consumer structure. This is not cloneable, but it
 /// may be safely shared so long as it is guaranteed that there is only one
 /// popper at a time (many pushers are allowed).
-pub struct Queue<T> {
+pub struct Queue<T: Send> {
     head: AtomicPtr<Node<T>>,
     tail: UnsafeCell<*mut Node<T>>,
 }
diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs
index f287712d9d4..13578ce0517 100644
--- a/src/libstd/sync/mpsc/oneshot.rs
+++ b/src/libstd/sync/mpsc/oneshot.rs
@@ -54,7 +54,7 @@ const DISCONNECTED: usize = 2;   // channel is disconnected OR upgraded
 // moves *from* a pointer, ownership of the token is transferred to
 // whoever changed the state.
 
-pub struct Packet<T> {
+pub struct Packet<T:Send> {
     // Internal state of the chan/port pair (stores the blocked task as well)
     state: AtomicUsize,
     // One-shot data slot location
@@ -64,7 +64,7 @@ pub struct Packet<T> {
     upgrade: MyUpgrade<T>,
 }
 
-pub enum Failure<T> {
+pub enum Failure<T:Send> {
     Empty,
     Disconnected,
     Upgraded(Receiver<T>),
@@ -76,13 +76,13 @@ pub enum UpgradeResult {
     UpWoke(SignalToken),
 }
 
-pub enum SelectionResult<T> {
+pub enum SelectionResult<T:Send> {
     SelCanceled,
     SelUpgraded(SignalToken, Receiver<T>),
     SelSuccess,
 }
 
-enum MyUpgrade<T> {
+enum MyUpgrade<T:Send> {
     NothingSent,
     SendUsed,
     GoUp(Receiver<T>),
diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs
index 0f936641cdc..b509b3472ee 100644
--- a/src/libstd/sync/mpsc/select.rs
+++ b/src/libstd/sync/mpsc/select.rs
@@ -80,7 +80,7 @@ impl !marker::Send for Select {}
 /// A handle to a receiver which is currently a member of a `Select` set of
 /// receivers.  This handle is used to keep the receiver in the set as well as
 /// interact with the underlying receiver.
-pub struct Handle<'rx, T:'rx> {
+pub struct Handle<'rx, T:Send+'rx> {
     /// The ID of this handle, used to compare against the return value of
     /// `Select::wait()`
     id: usize,
diff --git a/src/libstd/sync/mpsc/shared.rs b/src/libstd/sync/mpsc/shared.rs
index 8d14824d37f..f3930a8a5d6 100644
--- a/src/libstd/sync/mpsc/shared.rs
+++ b/src/libstd/sync/mpsc/shared.rs
@@ -40,7 +40,7 @@ const MAX_STEALS: isize = 5;
 #[cfg(not(test))]
 const MAX_STEALS: isize = 1 << 20;
 
-pub struct Packet<T> {
+pub struct Packet<T: Send> {
     queue: mpsc::Queue<T>,
     cnt: AtomicIsize, // How many items are on this channel
     steals: isize, // How many times has a port received without blocking?
diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/src/libstd/sync/mpsc/spsc_queue.rs
index 3fb13739aa7..cd6d1ee05c7 100644
--- a/src/libstd/sync/mpsc/spsc_queue.rs
+++ b/src/libstd/sync/mpsc/spsc_queue.rs
@@ -57,7 +57,7 @@ struct Node<T> {
 /// but it can be safely shared in an Arc if it is guaranteed that there
 /// is only one popper and one pusher touching the queue at any one point in
 /// time.
-pub struct Queue<T> {
+pub struct Queue<T: Send> {
     // consumer fields
     tail: UnsafeCell<*mut Node<T>>, // where to pop from
     tail_prev: AtomicPtr<Node<T>>, // where to pop from
diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs
index 5a1e05f9c15..a5a73314a6d 100644
--- a/src/libstd/sync/mpsc/stream.rs
+++ b/src/libstd/sync/mpsc/stream.rs
@@ -39,7 +39,7 @@ const MAX_STEALS: isize = 5;
 #[cfg(not(test))]
 const MAX_STEALS: isize = 1 << 20;
 
-pub struct Packet<T> {
+pub struct Packet<T:Send> {
     queue: spsc::Queue<Message<T>>, // internal queue for all message
 
     cnt: AtomicIsize, // How many items are on this channel
@@ -49,7 +49,7 @@ pub struct Packet<T> {
     port_dropped: AtomicBool, // flag if the channel has been destroyed.
 }
 
-pub enum Failure<T> {
+pub enum Failure<T:Send> {
     Empty,
     Disconnected,
     Upgraded(Receiver<T>),
@@ -61,7 +61,7 @@ pub enum UpgradeResult {
     UpWoke(SignalToken),
 }
 
-pub enum SelectionResult<T> {
+pub enum SelectionResult<T:Send> {
     SelSuccess,
     SelCanceled,
     SelUpgraded(SignalToken, Receiver<T>),
@@ -69,7 +69,7 @@ pub enum SelectionResult<T> {
 
 // Any message could contain an "upgrade request" to a new shared port, so the
 // internal queue it's a queue of T, but rather Message<T>
-enum Message<T> {
+enum Message<T:Send> {
     Data(T),
     GoUp(Receiver<T>),
 }
diff --git a/src/libstd/sync/mpsc/sync.rs b/src/libstd/sync/mpsc/sync.rs
index 33c1614e1b2..71236269487 100644
--- a/src/libstd/sync/mpsc/sync.rs
+++ b/src/libstd/sync/mpsc/sync.rs
@@ -47,7 +47,7 @@ use sync::mpsc::blocking::{self, WaitToken, SignalToken};
 use sync::mpsc::select::StartResult::{self, Installed, Abort};
 use sync::{Mutex, MutexGuard};
 
-pub struct Packet<T> {
+pub struct Packet<T: Send> {
     /// Only field outside of the mutex. Just done for kicks, but mainly because
     /// the other shared channel already had the code implemented
     channels: AtomicUsize,
diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs
index 2bf75cf1d37..b24cfbb6899 100644
--- a/src/libstd/sync/mutex.rs
+++ b/src/libstd/sync/mutex.rs
@@ -112,7 +112,7 @@ use fmt;
 /// *guard += 1;
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct Mutex<T> {
+pub struct Mutex<T: Send> {
     // Note that this static mutex is in a *box*, not inlined into the struct
     // itself. Once a native mutex has been used once, its address can never
     // change (it can't be moved). This mutex type can be safely moved at any
@@ -366,7 +366,7 @@ mod test {
     use sync::{Arc, Mutex, StaticMutex, MUTEX_INIT, Condvar};
     use thread;
 
-    struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
+    struct Packet<T: Send>(Arc<(Mutex<T>, Condvar)>);
 
     unsafe impl<T: Send> Send for Packet<T> {}
     unsafe impl<T> Sync for Packet<T> {}
diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs
index 53f18a57325..10077dfd1b8 100644
--- a/src/libstd/sys/common/helper_thread.rs
+++ b/src/libstd/sys/common/helper_thread.rs
@@ -38,7 +38,7 @@ use thread;
 ///
 /// The fields of this helper are all public, but they should not be used, this
 /// is for static initialization.
-pub struct Helper<M> {
+pub struct Helper<M:Send> {
     /// Internal lock which protects the remaining fields
     pub lock: StaticMutex,
     pub cond: StaticCondvar,
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index 57baeb1fb74..27b50fc9aaa 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -698,7 +698,7 @@ impl Drop for JoinHandle {
 /// permission.
 #[must_use = "thread will be immediately joined if `JoinGuard` is not used"]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct JoinGuard<'a, T: 'a> {
+pub struct JoinGuard<'a, T: Send + 'a> {
     inner: JoinInner<T>,
     _marker: PhantomData<&'a T>,
 }
diff --git a/src/test/auxiliary/issue-2526.rs b/src/test/auxiliary/issue-2526.rs
index 89b3b56121a..832665abdc2 100644
--- a/src/test/auxiliary/issue-2526.rs
+++ b/src/test/auxiliary/issue-2526.rs
@@ -15,7 +15,7 @@
 
 use std::marker;
 
-struct arc_destruct<T> {
+struct arc_destruct<T: Sync> {
     _data: int,
     _marker: marker::PhantomData<T>
 }
diff --git a/src/test/compile-fail/reject-specialized-drops-8142.rs b/src/test/compile-fail/reject-specialized-drops-8142.rs
new file mode 100644
index 00000000000..30264c9f218
--- /dev/null
+++ b/src/test/compile-fail/reject-specialized-drops-8142.rs
@@ -0,0 +1,79 @@
+// Copyright 2015 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.
+
+// Issue 8142: Test that Drop impls cannot be specialized beyond the
+// predicates attached to the struct/enum definition itself.
+
+#![feature(unsafe_destructor)]
+
+trait Bound { fn foo(&self) { } }
+struct K<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
+struct L<'l1,'l2> { x: &'l1 i8, y: &'l2 u8 }
+struct M<'m> { x: &'m i8 }
+struct N<'n> { x: &'n i8 }
+struct O<To> { x: *const To }
+struct P<Tp> { x: *const Tp }
+struct Q<Tq> { x: *const Tq }
+struct R<Tr> { x: *const Tr }
+struct S<Ts:Bound> { x: *const Ts }
+struct T<'t,Ts:'t> { x: &'t Ts }
+struct U;
+struct V<Tva, Tvb> { x: *const Tva, y: *const Tvb }
+struct W<'l1, 'l2> { x: &'l1 i8, y: &'l2 u8 }
+
+#[unsafe_destructor]
+impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> {                        // REJECT
+    //~^ ERROR The requirement `'adds_bnd : 'al` is added only by the Drop impl.
+    fn drop(&mut self) { } }
+
+#[unsafe_destructor]
+impl<'al,'adds_bnd>     Drop for L<'al,'adds_bnd> where 'adds_bnd:'al {    // REJECT
+    //~^ ERROR The requirement `'adds_bnd : 'al` is added only by the Drop impl.
+    fn drop(&mut self) { } }
+
+#[unsafe_destructor]
+impl<'ml>               Drop for M<'ml>         { fn drop(&mut self) { } } // ACCEPT
+
+#[unsafe_destructor]
+impl                    Drop for N<'static>     { fn drop(&mut self) { } } // REJECT
+//~^ ERROR Implementations of Drop cannot be specialized
+
+#[unsafe_destructor]
+impl<Cok_nobound> Drop for O<Cok_nobound> { fn drop(&mut self) { } } // ACCEPT
+
+#[unsafe_destructor]
+impl              Drop for P<i8>          { fn drop(&mut self) { } } // REJECT
+//~^ ERROR Implementations of Drop cannot be specialized
+
+#[unsafe_destructor]
+impl<Adds_bnd:Bound> Drop for Q<Adds_bnd> { fn drop(&mut self) { } } // REJECT
+//~^ ERROR The requirement `Adds_bnd : Bound` is added only by the Drop impl.
+
+#[unsafe_destructor]
+impl<'rbnd,Adds_rbnd:'rbnd> Drop for R<Adds_rbnd> { fn drop(&mut self) { } } // REJECT
+//~^ ERROR The requirement `Adds_rbnd : 'rbnd` is added only by the Drop impl.
+
+#[unsafe_destructor]
+impl<Bs:Bound>    Drop for S<Bs>          { fn drop(&mut self) { } } // ACCEPT
+
+#[unsafe_destructor]
+impl<'t,Bt:'t>    Drop for T<'t,Bt>       { fn drop(&mut self) { } } // ACCEPT
+
+impl              Drop for U              { fn drop(&mut self) { } } // ACCEPT
+
+#[unsafe_destructor]
+impl<One>         Drop for V<One,One>     { fn drop(&mut self) { } } // REJECT
+//~^ERROR Implementations of Drop cannot be specialized
+
+#[unsafe_destructor]
+impl<'lw>         Drop for W<'lw,'lw>     { fn drop(&mut self) { } } // REJECT
+//~^ERROR Implementations of Drop cannot be specialized
+
+pub fn main() { }
diff --git a/src/test/run-pass/issue-15858.rs b/src/test/run-pass/issue-15858.rs
index 9b300deaa49..265db3fe133 100644
--- a/src/test/run-pass/issue-15858.rs
+++ b/src/test/run-pass/issue-15858.rs
@@ -25,7 +25,7 @@ impl Bar for BarImpl {
 }
 
 
-struct Foo<B>(B);
+struct Foo<B: Bar>(B);
 
 #[unsafe_destructor]
 impl<B: Bar> Drop for Foo<B> {
diff --git a/src/test/run-pass/issue-15924.rs b/src/test/run-pass/issue-15924.rs
index 6af07c422ef..e544585745d 100644
--- a/src/test/run-pass/issue-15924.rs
+++ b/src/test/run-pass/issue-15924.rs
@@ -18,7 +18,7 @@ use std::fmt;
 use serialize::{Encoder, Encodable};
 use serialize::json;
 
-struct Foo<T> {
+struct Foo<T: Encodable> {
     v: T,
 }
 
diff --git a/src/test/run-pass/issue-2718.rs b/src/test/run-pass/issue-2718.rs
index 8d0e0654933..7ca0ee01015 100644
--- a/src/test/run-pass/issue-2718.rs
+++ b/src/test/run-pass/issue-2718.rs
@@ -162,7 +162,7 @@ pub mod pipes {
         }
     }
 
-    pub struct send_packet<T> {
+    pub struct send_packet<T:Send> {
         p: Option<*const packet<T>>,
     }
 
@@ -192,7 +192,7 @@ pub mod pipes {
         }
     }
 
-    pub struct recv_packet<T> {
+    pub struct recv_packet<T:Send> {
         p: Option<*const packet<T>>,
     }
 
diff --git a/src/test/run-pass/issue-4252.rs b/src/test/run-pass/issue-4252.rs
index 9d5f8576c63..08ee955cabb 100644
--- a/src/test/run-pass/issue-4252.rs
+++ b/src/test/run-pass/issue-4252.rs
@@ -21,7 +21,7 @@ trait X {
 struct Y(int);
 
 #[derive(Debug)]
-struct Z<T> {
+struct Z<T: X+std::fmt::Debug> {
     x: T
 }