diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-03-24 15:27:14 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-03-24 15:27:14 -0700 |
| commit | 3b13b9c2b4e72d08cb1c68024ccc4f50001f4878 (patch) | |
| tree | d4efd6426beeeee1f0c543cfe345b9625285f46e | |
| parent | 91b633aa038008fdbee658a10182afdd794d2aa6 (diff) | |
| parent | 1955e052675d4457432da85a00db0ae55be64e83 (diff) | |
| download | rust-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
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 } |
