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
This commit is contained in:
commit
3b13b9c2b4
27 changed files with 465 additions and 63 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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> {}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
use std::marker;
|
||||
|
||||
struct arc_destruct<T> {
|
||||
struct arc_destruct<T: Sync> {
|
||||
_data: int,
|
||||
_marker: marker::PhantomData<T>
|
||||
}
|
||||
|
|
|
|||
79
src/test/compile-fail/reject-specialized-drops-8142.rs
Normal file
79
src/test/compile-fail/reject-specialized-drops-8142.rs
Normal file
|
|
@ -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() { }
|
||||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use std::fmt;
|
|||
use serialize::{Encoder, Encodable};
|
||||
use serialize::json;
|
||||
|
||||
struct Foo<T> {
|
||||
struct Foo<T: Encodable> {
|
||||
v: T,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ trait X {
|
|||
struct Y(int);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Z<T> {
|
||||
struct Z<T: X+std::fmt::Debug> {
|
||||
x: T
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue