Auto merge of #28861 - pnkfelix:fsk-nonparam-dropck-issue28498, r=arielb1
implement RFC 1238: nonparametric dropck. cc #28498 cc @nikomatsakis
This commit is contained in:
commit
87cd2c0827
@ -115,13 +115,162 @@ section:
|
||||
**For a generic type to soundly implement drop, its generics arguments must
|
||||
strictly outlive it.**
|
||||
|
||||
This rule is sufficient but not necessary to satisfy the drop checker. That is,
|
||||
if your type obeys this rule then it's definitely sound to drop. However
|
||||
there are special cases where you can fail to satisfy this, but still
|
||||
successfully pass the borrow checker. These are the precise rules that are
|
||||
currently up in the air.
|
||||
Obeying this rule is (usually) necessary to satisfy the borrow
|
||||
checker; obeying it is sufficient but not necessary to be
|
||||
sound. That is, if your type obeys this rule then it's definitely
|
||||
sound to drop.
|
||||
|
||||
The reason that it is not always necessary to satisfy the above rule
|
||||
is that some Drop implementations will not access borrowed data even
|
||||
though their type gives them the capability for such access.
|
||||
|
||||
For example, this variant of the above `Inspector` example will never
|
||||
accessed borrowed data:
|
||||
|
||||
```rust,ignore
|
||||
struct Inspector<'a>(&'a u8, &'static str);
|
||||
|
||||
impl<'a> Drop for Inspector<'a> {
|
||||
fn drop(&mut self) {
|
||||
println!("Inspector(_, {}) knows when *not* to inspect.", self.1);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (inspector, days);
|
||||
days = Box::new(1);
|
||||
inspector = Inspector(&days, "gadget");
|
||||
// Let's say `days` happens to get dropped first.
|
||||
// Even when Inspector is dropped, its destructor will not access the
|
||||
// borrowed `days`.
|
||||
}
|
||||
```
|
||||
|
||||
Likewise, this variant will also never access borrowed data:
|
||||
|
||||
```rust,ignore
|
||||
use std::fmt;
|
||||
|
||||
struct Inspector<T: fmt::Display>(T, &'static str);
|
||||
|
||||
impl<T: fmt::Display> Drop for Inspector<T> {
|
||||
fn drop(&mut self) {
|
||||
println!("Inspector(_, {}) knows when *not* to inspect.", self.1);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (inspector, days): (Inspector<&u8>, Box<u8>);
|
||||
days = Box::new(1);
|
||||
inspector = Inspector(&days, "gadget");
|
||||
// Let's say `days` happens to get dropped first.
|
||||
// Even when Inspector is dropped, its destructor will not access the
|
||||
// borrowed `days`.
|
||||
}
|
||||
```
|
||||
|
||||
However, *both* of the above variants are rejected by the borrow
|
||||
checker during the analysis of `fn main`, saying that `days` does not
|
||||
live long enough.
|
||||
|
||||
The reason is that the borrow checking analysis of `main` does not
|
||||
know about the internals of each Inspector's Drop implementation. As
|
||||
far as the borrow checker knows while it is analyzing `main`, the body
|
||||
of an inspector's destructor might access that borrowed data.
|
||||
|
||||
Therefore, the drop checker forces all borrowed data in a value to
|
||||
strictly outlive that value.
|
||||
|
||||
# An Escape Hatch
|
||||
|
||||
The precise rules that govern drop checking may be less restrictive in
|
||||
the future.
|
||||
|
||||
The current analysis is deliberately conservative and trivial; it forces all
|
||||
borrowed data in a value to outlive that value, which is certainly sound.
|
||||
|
||||
Future versions of the language may make the analysis more precise, to
|
||||
reduce the number of cases where sound code is rejected as unsafe.
|
||||
This would help address cases such as the two Inspectors above that
|
||||
know not to inspect during destruction.
|
||||
|
||||
In the meantime, there is an unstable attribute that one can use to
|
||||
assert (unsafely) that a generic type's destructor is *guaranteed* to
|
||||
not access any expired data, even if its type gives it the capability
|
||||
to do so.
|
||||
|
||||
That attribute is called `unsafe_destructor_blind_to_params`.
|
||||
To deploy it on the Inspector example from above, we would write:
|
||||
|
||||
```rust,ignore
|
||||
struct Inspector<'a>(&'a u8, &'static str);
|
||||
|
||||
impl<'a> Drop for Inspector<'a> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
println!("Inspector(_, {}) knows when *not* to inspect.", self.1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This attribute has the word `unsafe` in it because the compiler is not
|
||||
checking the implicit assertion that no potentially expired data
|
||||
(e.g. `self.0` above) is accessed.
|
||||
|
||||
It is sometimes obvious that no such access can occur, like the case above.
|
||||
However, when dealing with a generic type parameter, such access can
|
||||
occur indirectly. Examples of such indirect access are:
|
||||
* invoking a callback,
|
||||
* via a trait method call.
|
||||
|
||||
(Future changes to the language, such as impl specialization, may add
|
||||
other avenues for such indirect access.)
|
||||
|
||||
Here is an example of invoking a callback:
|
||||
|
||||
```rust,ignore
|
||||
struct Inspector<T>(T, &'static str, Box<for <'r> fn(&'r T) -> String>);
|
||||
|
||||
impl<T> Drop for Inspector<T> {
|
||||
fn drop(&mut self) {
|
||||
// The `self.2` call could access a borrow e.g. if `T` is `&'a _`.
|
||||
println!("Inspector({}, {}) unwittingly inspects expired data.",
|
||||
(self.2)(&self.0), self.1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is an example of a trait method call:
|
||||
|
||||
```rust,ignore
|
||||
use std::fmt;
|
||||
|
||||
struct Inspector<T: fmt::Display>(T, &'static str);
|
||||
|
||||
impl<T: fmt::Display> Drop for Inspector<T> {
|
||||
fn drop(&mut self) {
|
||||
// There is a hidden call to `<T as Display>::fmt` below, which
|
||||
// could access a borrow e.g. if `T` is `&'a _`
|
||||
println!("Inspector({}, {}) unwittingly inspects expired data.",
|
||||
self.0, self.1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And of course, all of these accesses could be further hidden within
|
||||
some other method invoked by the destructor, rather than being written
|
||||
directly within it.
|
||||
|
||||
In all of the above cases where the `&'a u8` is accessed in the
|
||||
destructor, adding the `#[unsafe_destructor_blind_to_params]`
|
||||
attribute makes the type vulnerable to misuse that the borrower
|
||||
checker will not catch, inviting havoc. It is better to avoid adding
|
||||
the attribute.
|
||||
|
||||
# Is that all about drop checker?
|
||||
|
||||
It turns out that when writing unsafe code, we generally don't need to
|
||||
worry at all about doing the right thing for the drop checker. However there
|
||||
is one special case that you need to worry about, which we will look at in
|
||||
the next section.
|
||||
|
||||
|
@ -1929,6 +1929,20 @@ macro scope.
|
||||
- `simd` - on certain tuple structs, derive the arithmetic operators, which
|
||||
lower to the target's SIMD instructions, if any; the `simd` feature gate
|
||||
is necessary to use this attribute.
|
||||
- `unsafe_destructor_blind_to_params` - on `Drop::drop` method, asserts that the
|
||||
destructor code (and all potential specializations of that code) will
|
||||
never attempt to read from nor write to any references with lifetimes
|
||||
that come in via generic parameters. This is a constraint we cannot
|
||||
currently express via the type system, and therefore we rely on the
|
||||
programmer to assert that it holds. Adding this to a Drop impl causes
|
||||
the associated destructor to be considered "uninteresting" by the
|
||||
Drop-Check rule, and thus it can help sidestep data ordering
|
||||
constraints that would otherwise be introduced by the Drop-Check
|
||||
rule. Such sidestepping of the constraints, if done incorrectly, can
|
||||
lead to undefined behavior (in the form of reading or writing to data
|
||||
outside of its dynamic extent), and thus this attribute has the word
|
||||
"unsafe" in its name. To use this, the
|
||||
`unsafe_destructor_blind_to_params` feature gate must be enabled.
|
||||
- `unsafe_no_drop_flag` - on structs, remove the flag that prevents
|
||||
destructors from being run twice. Destructors might be run multiple times on
|
||||
the same object with this attribute. To use this, the `unsafe_no_drop_flag` feature
|
||||
|
@ -550,6 +550,7 @@ impl<T: ?Sized> Drop for Arc<T> {
|
||||
///
|
||||
/// } // implicit drop
|
||||
/// ```
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
// This structure has #[unsafe_no_drop_flag], so this drop glue may run
|
||||
|
@ -94,6 +94,11 @@
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unique)]
|
||||
#![feature(unsafe_no_drop_flag, filling_drop)]
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_features)]
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_attributes)]
|
||||
#![feature(dropck_parametricity)]
|
||||
#![feature(unsize)]
|
||||
#![feature(core_slice_ext)]
|
||||
#![feature(core_str_ext)]
|
||||
|
@ -445,6 +445,7 @@ pub fn unsafe_no_drop_flag_needs_drop(&self) -> bool {
|
||||
}
|
||||
|
||||
impl<T> Drop for RawVec<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
/// Frees the memory owned by the RawVec *without* trying to Drop its contents.
|
||||
fn drop(&mut self) {
|
||||
let elem_size = mem::size_of::<T>();
|
||||
|
@ -451,6 +451,7 @@ impl<T: ?Sized> Drop for Rc<T> {
|
||||
///
|
||||
/// } // implicit drop
|
||||
/// ```
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let ptr = *self._ptr;
|
||||
|
@ -38,8 +38,14 @@
|
||||
#![feature(ptr_as_ref)]
|
||||
#![feature(raw)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(dropck_parametricity)]
|
||||
#![cfg_attr(test, feature(test))]
|
||||
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_features)]
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_attributes)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
@ -510,6 +516,7 @@ fn grow(&self) {
|
||||
}
|
||||
|
||||
impl<T> Drop for TypedArena<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Determine how much was filled.
|
||||
|
@ -275,12 +275,14 @@ fn next_back(&mut self) -> Option<T> {
|
||||
}
|
||||
|
||||
impl<T> Drop for RawItems<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
for _ in self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Drop for Node<K, V> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
if self.keys.is_null() ||
|
||||
(unsafe { self.keys.get() as *const K as usize == mem::POST_DROP_USIZE })
|
||||
@ -1419,6 +1421,7 @@ fn next_edge_back(&mut self) -> Option<Node<K, V>> {
|
||||
}
|
||||
|
||||
impl<K, V> Drop for MoveTraversalImpl<K, V> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
// We need to cleanup the stored values manually, as the RawItems destructor would run
|
||||
// after our deallocation.
|
||||
|
@ -32,6 +32,11 @@
|
||||
#![allow(trivial_casts)]
|
||||
#![cfg_attr(test, allow(deprecated))] // rand
|
||||
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_features)]
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_attributes)]
|
||||
|
||||
#![feature(alloc)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(box_syntax)]
|
||||
@ -59,6 +64,7 @@
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unicode)]
|
||||
#![feature(unique)]
|
||||
#![feature(dropck_parametricity)]
|
||||
#![feature(unsafe_no_drop_flag, filling_drop)]
|
||||
#![feature(decode_utf16)]
|
||||
#![feature(utf8_error)]
|
||||
|
@ -655,6 +655,7 @@ pub fn split_off(&mut self, at: usize) -> LinkedList<T> {
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Drop for LinkedList<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
// Dissolve the linked_list in a loop.
|
||||
// Just dropping the list_head can lead to stack exhaustion
|
||||
|
@ -1385,6 +1385,7 @@ fn cmp(&self, other: &Vec<T>) -> Ordering {
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Drop for Vec<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
// NOTE: this is currently abusing the fact that ZSTs can't impl Drop.
|
||||
// Or rather, that impl'ing Drop makes them not zero-sized. This is
|
||||
|
@ -64,6 +64,7 @@ fn clone(&self) -> VecDeque<T> {
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Drop for VecDeque<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
self.clear();
|
||||
// RawVec handles deallocation
|
||||
|
@ -566,84 +566,34 @@ macro_rules! hash { ($e:expr) => { $e.hash(state) } }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this ADT is a dtorck type, i.e. whether it being
|
||||
/// safe for destruction requires it to be alive
|
||||
/// Returns true if this ADT is a dtorck type.
|
||||
///
|
||||
/// Invoking the destructor of a dtorck type during usual cleanup
|
||||
/// (e.g. the glue emitted for stack unwinding) requires all
|
||||
/// lifetimes in the type-structure of `adt` to strictly outlive
|
||||
/// the adt value itself.
|
||||
///
|
||||
/// If `adt` is not dtorck, then the adt's destructor can be
|
||||
/// invoked even when there are lifetimes in the type-structure of
|
||||
/// `adt` that do not strictly outlive the adt value itself.
|
||||
/// (This allows programs to make cyclic structures without
|
||||
/// resorting to unasfe means; see RFCs 769 and 1238).
|
||||
pub fn is_adt_dtorck(&self, adt: ty::AdtDef<'tcx>) -> bool {
|
||||
let dtor_method = match adt.destructor() {
|
||||
Some(dtor) => dtor,
|
||||
None => return false
|
||||
};
|
||||
let impl_did = self.impl_of_method(dtor_method).unwrap_or_else(|| {
|
||||
self.sess.bug(&format!("no Drop impl for the dtor of `{:?}`", adt))
|
||||
});
|
||||
let generics = adt.type_scheme(self).generics;
|
||||
|
||||
// In `impl<'a> Drop ...`, we automatically assume
|
||||
// `'a` is meaningful and thus represents a bound
|
||||
// through which we could reach borrowed data.
|
||||
// RFC 1238: if the destructor method is tagged with the
|
||||
// attribute `unsafe_destructor_blind_to_params`, then the
|
||||
// compiler is being instructed to *assume* that the
|
||||
// destructor will not access borrowed data,
|
||||
// even if such data is otherwise reachable.
|
||||
//
|
||||
// FIXME (pnkfelix): In the future it would be good to
|
||||
// extend the language to allow the user to express,
|
||||
// in the impl signature, that a lifetime is not
|
||||
// actually used (something like `where 'a: ?Live`).
|
||||
if generics.has_region_params(subst::TypeSpace) {
|
||||
debug!("typ: {:?} has interesting dtor due to region params",
|
||||
adt);
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut seen_items = Vec::new();
|
||||
let mut items_to_inspect = vec![impl_did];
|
||||
while let Some(item_def_id) = items_to_inspect.pop() {
|
||||
if seen_items.contains(&item_def_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for pred in self.lookup_predicates(item_def_id).predicates {
|
||||
let result = match pred {
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::Projection(..) => {
|
||||
// For now, assume all these where-clauses
|
||||
// may give drop implementation capabilty
|
||||
// to access borrowed data.
|
||||
true
|
||||
}
|
||||
|
||||
ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
|
||||
let def_id = t_pred.trait_ref.def_id;
|
||||
if self.trait_items(def_id).len() != 0 {
|
||||
// If trait has items, assume it adds
|
||||
// capability to access borrowed data.
|
||||
true
|
||||
} else {
|
||||
// Trait without items is itself
|
||||
// uninteresting from POV of dropck.
|
||||
//
|
||||
// However, may have parent w/ items;
|
||||
// so schedule checking of predicates,
|
||||
items_to_inspect.push(def_id);
|
||||
// and say "no capability found" for now.
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if result {
|
||||
debug!("typ: {:?} has interesting dtor due to generic preds, e.g. {:?}",
|
||||
adt, pred);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
seen_items.push(item_def_id);
|
||||
}
|
||||
|
||||
debug!("typ: {:?} is dtorck-safe", adt);
|
||||
false
|
||||
// Such access can be in plain sight (e.g. dereferencing
|
||||
// `*foo.0` of `Foo<'a>(&'a u32)`) or indirectly hidden
|
||||
// (e.g. calling `foo.0.clone()` of `Foo<T:Clone>`).
|
||||
return !self.has_attr(dtor_method, "unsafe_destructor_blind_to_params");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,19 +217,16 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
|
||||
///
|
||||
/// ----
|
||||
///
|
||||
/// The Drop Check Rule is the following:
|
||||
/// The simplified (*) 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,
|
||||
/// * (1.) `D` has a lifetime- or type-parametric Drop implementation,
|
||||
/// (where that `Drop` implementation does not opt-out of
|
||||
/// this check via the `unsafe_destructor_blind_to_params`
|
||||
/// attribute), and
|
||||
/// * (2.) the structure of `D` can reach a reference of type `&'a _`,
|
||||
///
|
||||
/// then 'a must strictly outlive the scope of v.
|
||||
///
|
||||
@ -237,6 +234,35 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
|
||||
///
|
||||
/// This function is meant to by applied to the type for every
|
||||
/// expression in the program.
|
||||
///
|
||||
/// ----
|
||||
///
|
||||
/// (*) The qualifier "simplified" is attached to the above
|
||||
/// definition of the Drop Check Rule, because it is a simplification
|
||||
/// of the original Drop Check rule, which attempted to prove that
|
||||
/// some `Drop` implementations could not possibly access data even if
|
||||
/// it was technically reachable, due to parametricity.
|
||||
///
|
||||
/// However, (1.) parametricity on its own turned out to be a
|
||||
/// necessary but insufficient condition, and (2.) future changes to
|
||||
/// the language are expected to make it impossible to ensure that a
|
||||
/// `Drop` implementation is actually parametric with respect to any
|
||||
/// particular type parameter. (In particular, impl specialization is
|
||||
/// expected to break the needed parametricity property beyond
|
||||
/// repair.)
|
||||
///
|
||||
/// Therefore we have scaled back Drop-Check to a more conservative
|
||||
/// rule that does not attempt to deduce whether a `Drop`
|
||||
/// implementation could not possible access data of a given lifetime;
|
||||
/// instead Drop-Check now simply assumes that if a destructor has
|
||||
/// access (direct or indirect) to a lifetime parameter, then that
|
||||
/// lifetime must be forced to outlive that destructor's dynamic
|
||||
/// extent. We then provide the `unsafe_destructor_blind_to_params`
|
||||
/// attribute as a way for destructor implementations to opt-out of
|
||||
/// this conservative assumption (and thus assume the obligation of
|
||||
/// ensuring that they do not access data nor invoke methods of
|
||||
/// values that have been previously dropped).
|
||||
///
|
||||
pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
||||
typ: ty::Ty<'tcx>,
|
||||
span: Span,
|
||||
@ -356,13 +382,18 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
|
||||
// borrowed data reachable via `typ` must outlive the parent
|
||||
// of `scope`. This is handled below.
|
||||
//
|
||||
// However, there is an important special case: by
|
||||
// parametricity, any generic type parameters have *no* trait
|
||||
// bounds in the Drop impl can not be used in any way (apart
|
||||
// from being dropped), and thus we can treat data borrowed
|
||||
// via such type parameters remains unreachable.
|
||||
// However, there is an important special case: for any Drop
|
||||
// impl that is tagged as "blind" to their parameters,
|
||||
// we assume that data borrowed via such type parameters
|
||||
// remains unreachable via that Drop impl.
|
||||
//
|
||||
// For example, consider:
|
||||
//
|
||||
// ```rust
|
||||
// #[unsafe_destructor_blind_to_params]
|
||||
// impl<T> Drop for Vec<T> { ... }
|
||||
// ```
|
||||
//
|
||||
// For example, consider `impl<T> Drop for Vec<T> { ... }`,
|
||||
// which does have to be able to drop instances of `T`, but
|
||||
// otherwise cannot read data from `T`.
|
||||
//
|
||||
@ -370,16 +401,6 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
|
||||
// unbounded type parameter `T`, we must resume the recursive
|
||||
// analysis on `T` (since it would be ignored by
|
||||
// type_must_outlive).
|
||||
//
|
||||
// FIXME (pnkfelix): Long term, we could be smart and actually
|
||||
// feed which generic parameters can be ignored *into* `fn
|
||||
// type_must_outlive` (or some generalization thereof). But
|
||||
// for the short term, it probably covers most cases of
|
||||
// interest to just special case Drop impls where: (1.) there
|
||||
// are no generic lifetime parameters and (2.) *all* generic
|
||||
// type parameters are unbounded. If both conditions hold, we
|
||||
// simply skip the `type_must_outlive` call entirely (but
|
||||
// resume the recursive checking of the type-substructure).
|
||||
if has_dtor_of_interest(tcx, ty) {
|
||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
||||
{}ty: {} - is a dtorck type!",
|
||||
|
@ -999,6 +999,7 @@ fn clone(&self) -> RawTable<K, V> {
|
||||
}
|
||||
|
||||
impl<K, V> Drop for RawTable<K, V> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
if self.capacity == 0 || self.capacity == mem::POST_DROP_USIZE {
|
||||
return;
|
||||
|
@ -199,6 +199,11 @@
|
||||
test(no_crate_inject, attr(deny(warnings))),
|
||||
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))]
|
||||
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_features)]
|
||||
// SNAP 1af31d4
|
||||
#![allow(unused_attributes)]
|
||||
|
||||
#![feature(alloc)]
|
||||
#![feature(allow_internal_unstable)]
|
||||
#![feature(associated_consts)]
|
||||
@ -241,6 +246,7 @@
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unicode)]
|
||||
#![feature(unique)]
|
||||
#![feature(dropck_parametricity)]
|
||||
#![feature(unsafe_no_drop_flag, filling_drop)]
|
||||
#![feature(decode_utf16)]
|
||||
#![feature(unwind_attributes)]
|
||||
|
@ -136,6 +136,10 @@
|
||||
// switch to Accepted; see RFC 320)
|
||||
("unsafe_no_drop_flag", "1.0.0", None, Active),
|
||||
|
||||
// Allows using the unsafe_destructor_blind_to_params attribute;
|
||||
// RFC 1238
|
||||
("dropck_parametricity", "1.3.0", Some(28498), Active),
|
||||
|
||||
// Allows the use of custom attributes; RFC 572
|
||||
("custom_attribute", "1.0.0", None, Active),
|
||||
|
||||
@ -339,6 +343,11 @@ enum Status {
|
||||
("unsafe_no_drop_flag", Whitelisted, Gated("unsafe_no_drop_flag",
|
||||
"unsafe_no_drop_flag has unstable semantics \
|
||||
and may be removed in the future")),
|
||||
("unsafe_destructor_blind_to_params",
|
||||
Normal,
|
||||
Gated("dropck_parametricity",
|
||||
"unsafe_destructor_blind_to_params has unstable semantics \
|
||||
and may be removed in the future")),
|
||||
("unwind", Whitelisted, Gated("unwind_attributes", "#[unwind] is experimental")),
|
||||
|
||||
// used in resolve
|
||||
|
40
src/test/compile-fail/feature-gate-dropck-ugeh.rs
Normal file
40
src/test/compile-fail/feature-gate-dropck-ugeh.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
// Ensure that attempts to use the unsafe attribute are feature-gated.
|
||||
|
||||
// Example adapted from RFC 1238 text (just left out the feature gate).
|
||||
|
||||
// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
|
||||
// #example-of-the-unguarded-escape-hatch
|
||||
|
||||
// #![feature(dropck_parametricity)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
struct Concrete<'a>(u32, Cell<Option<&'a Concrete<'a>>>);
|
||||
|
||||
struct Foo<T> { data: Vec<T> }
|
||||
|
||||
impl<T> Drop for Foo<T> {
|
||||
#[unsafe_destructor_blind_to_params] // This is the UGEH attribute
|
||||
//~^ ERROR unsafe_destructor_blind_to_params has unstable semantics
|
||||
fn drop(&mut self) { }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut foo = Foo { data: Vec::new() };
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
|
||||
foo.data[0].1.set(Some(&foo.data[1]));
|
||||
foo.data[1].1.set(Some(&foo.data[0]));
|
||||
}
|
||||
|
48
src/test/compile-fail/issue28498-reject-ex1.rs
Normal file
48
src/test/compile-fail/issue28498-reject-ex1.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
// Example taken from RFC 1238 text
|
||||
|
||||
// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
|
||||
// #examples-of-code-that-will-start-to-be-rejected
|
||||
|
||||
// Compare against test/run-pass/issue28498-must-work-ex2.rs
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
struct Concrete<'a>(u32, Cell<Option<&'a Concrete<'a>>>);
|
||||
|
||||
struct Foo<T> { data: Vec<T> }
|
||||
|
||||
fn potentially_specialized_wrt_t<T>(t: &T) {
|
||||
// Hypothetical code that does one thing for generic T and then is
|
||||
// specialized for T == Concrete (and the specialized form can
|
||||
// then access a reference held in concrete tuple).
|
||||
//
|
||||
// (We don't have specialization yet, but we want to allow for it
|
||||
// in the future.)
|
||||
}
|
||||
|
||||
impl<T> Drop for Foo<T> {
|
||||
fn drop(&mut self) {
|
||||
potentially_specialized_wrt_t(&self.data[0])
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut foo = Foo { data: Vec::new() };
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
|
||||
foo.data[0].1.set(Some(&foo.data[1]));
|
||||
//~^ ERROR `foo.data` does not live long enough
|
||||
foo.data[1].1.set(Some(&foo.data[0]));
|
||||
//~^ ERROR `foo.data` does not live long enough
|
||||
}
|
48
src/test/compile-fail/issue28498-reject-lifetime-param.rs
Normal file
48
src/test/compile-fail/issue28498-reject-lifetime-param.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
// Demonstrate that having a lifetime param causes dropck to reject code
|
||||
// that might indirectly access previously dropped value.
|
||||
//
|
||||
// Compare with run-pass/issue28498-ugeh-with-lifetime-param.rs
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ScribbleOnDrop(String);
|
||||
|
||||
impl Drop for ScribbleOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0 = format!("DROPPED");
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo<'a>(u32, &'a ScribbleOnDrop);
|
||||
|
||||
impl<'a> Drop for Foo<'a> {
|
||||
fn drop(&mut self) {
|
||||
// Use of `unsafe_destructor_blind_to_params` is unsound,
|
||||
// because destructor accesses borrowed data in `self.1`
|
||||
// and we must force that to strictly outlive `self`.
|
||||
println!("Dropping Foo({}, {:?})", self.0, self.1);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (last_dropped, foo0);
|
||||
let (foo1, first_dropped);
|
||||
|
||||
last_dropped = ScribbleOnDrop(format!("last"));
|
||||
first_dropped = ScribbleOnDrop(format!("first"));
|
||||
foo0 = Foo(0, &last_dropped);
|
||||
//~^ ERROR `last_dropped` does not live long enough
|
||||
foo1 = Foo(1, &first_dropped);
|
||||
//~^ ERROR `first_dropped` does not live long enough
|
||||
|
||||
println!("foo0.1: {:?} foo1.1: {:?}", foo0.1, foo1.1);
|
||||
}
|
50
src/test/compile-fail/issue28498-reject-passed-to-fn.rs
Normal file
50
src/test/compile-fail/issue28498-reject-passed-to-fn.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// 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.
|
||||
|
||||
// Demonstrate that a type param in negative position causes dropck to reject code
|
||||
// that might indirectly access previously dropped value.
|
||||
//
|
||||
// Compare with run-pass/issue28498-ugeh-with-passed-to-fn.rs
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ScribbleOnDrop(String);
|
||||
|
||||
impl Drop for ScribbleOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0 = format!("DROPPED");
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo<T>(u32, T, Box<for <'r> fn(&'r T) -> String>);
|
||||
|
||||
impl<T> Drop for Foo<T> {
|
||||
fn drop(&mut self) {
|
||||
// Use of `unsafe_destructor_blind_to_params` is unsound,
|
||||
// because we pass `T` to the callback in `self.2`
|
||||
// below, and thus potentially read from borrowed data.
|
||||
println!("Dropping Foo({}, {})", self.0, (self.2)(&self.1));
|
||||
}
|
||||
}
|
||||
|
||||
fn callback(s: & &ScribbleOnDrop) -> String { format!("{:?}", s) }
|
||||
|
||||
fn main() {
|
||||
let (last_dropped, foo0);
|
||||
let (foo1, first_dropped);
|
||||
|
||||
last_dropped = ScribbleOnDrop(format!("last"));
|
||||
first_dropped = ScribbleOnDrop(format!("first"));
|
||||
foo0 = Foo(0, &last_dropped, Box::new(callback));
|
||||
//~^ ERROR `last_dropped` does not live long enough
|
||||
foo1 = Foo(1, &first_dropped, Box::new(callback));
|
||||
//~^ ERROR `first_dropped` does not live long enough
|
||||
|
||||
println!("foo0.1: {:?} foo1.1: {:?}", foo0.1, foo1.1);
|
||||
}
|
50
src/test/compile-fail/issue28498-reject-trait-bound.rs
Normal file
50
src/test/compile-fail/issue28498-reject-trait-bound.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// 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.
|
||||
|
||||
// Demonstrate that having a trait bound causes dropck to reject code
|
||||
// that might indirectly access previously dropped value.
|
||||
//
|
||||
// Compare with run-pass/issue28498-ugeh-with-trait-bound.rs
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ScribbleOnDrop(String);
|
||||
|
||||
impl Drop for ScribbleOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0 = format!("DROPPED");
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo<T:fmt::Debug>(u32, T);
|
||||
|
||||
impl<T:fmt::Debug> Drop for Foo<T> {
|
||||
fn drop(&mut self) {
|
||||
// Use of `unsafe_destructor_blind_to_params` is unsound,
|
||||
// because we access `T` fmt method when we pass `self.1`
|
||||
// below, and thus potentially read from borrowed data.
|
||||
println!("Dropping Foo({}, {:?})", self.0, self.1);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (last_dropped, foo0);
|
||||
let (foo1, first_dropped);
|
||||
|
||||
last_dropped = ScribbleOnDrop(format!("last"));
|
||||
first_dropped = ScribbleOnDrop(format!("first"));
|
||||
foo0 = Foo(0, &last_dropped);
|
||||
//~^ ERROR `last_dropped` does not live long enough
|
||||
foo1 = Foo(1, &first_dropped);
|
||||
//~^ ERROR `first_dropped` does not live long enough
|
||||
|
||||
println!("foo0.1: {:?} foo1.1: {:?}", foo0.1, foo1.1);
|
||||
}
|
@ -13,6 +13,8 @@
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
#![feature(dropck_parametricity)]
|
||||
|
||||
trait UserDefined { }
|
||||
|
||||
impl UserDefined for i32 { }
|
||||
@ -26,7 +28,10 @@ impl<'a, T> UserDefined for &'a T { }
|
||||
macro_rules! impl_drop {
|
||||
($Bound:ident, $Id:ident) => {
|
||||
struct $Id<T:$Bound>(T);
|
||||
impl <T:$Bound> Drop for $Id<T> { fn drop(&mut self) { } }
|
||||
impl <T:$Bound> Drop for $Id<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
27
src/test/run-pass/issue28498-must-work-ex1.rs
Normal file
27
src/test/run-pass/issue28498-must-work-ex1.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// 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.
|
||||
|
||||
// Example taken from RFC 1238 text
|
||||
|
||||
// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
|
||||
// #examples-of-code-that-must-continue-to-work
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
struct Concrete<'a>(u32, Cell<Option<&'a Concrete<'a>>>);
|
||||
|
||||
fn main() {
|
||||
let mut data = Vec::new();
|
||||
data.push(Concrete(0, Cell::new(None)));
|
||||
data.push(Concrete(0, Cell::new(None)));
|
||||
|
||||
data[0].1.set(Some(&data[1]));
|
||||
data[1].1.set(Some(&data[0]));
|
||||
}
|
30
src/test/run-pass/issue28498-must-work-ex2.rs
Normal file
30
src/test/run-pass/issue28498-must-work-ex2.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
// Example taken from RFC 1238 text
|
||||
|
||||
// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
|
||||
// #examples-of-code-that-must-continue-to-work
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
struct Concrete<'a>(u32, Cell<Option<&'a Concrete<'a>>>);
|
||||
|
||||
struct Foo<T> { data: Vec<T> }
|
||||
|
||||
fn main() {
|
||||
let mut foo = Foo { data: Vec::new() };
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
|
||||
foo.data[0].1.set(Some(&foo.data[1]));
|
||||
foo.data[1].1.set(Some(&foo.data[0]));
|
||||
}
|
||||
|
37
src/test/run-pass/issue28498-ugeh-ex1.rs
Normal file
37
src/test/run-pass/issue28498-ugeh-ex1.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
// Example taken from RFC 1238 text
|
||||
|
||||
// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
|
||||
// #example-of-the-unguarded-escape-hatch
|
||||
|
||||
#![feature(dropck_parametricity)]
|
||||
use std::cell::Cell;
|
||||
|
||||
struct Concrete<'a>(u32, Cell<Option<&'a Concrete<'a>>>);
|
||||
|
||||
struct Foo<T> { data: Vec<T> }
|
||||
|
||||
impl<T> Drop for Foo<T> {
|
||||
// Below is the UGEH attribute
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) { }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut foo = Foo { data: Vec::new() };
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
foo.data.push(Concrete(0, Cell::new(None)));
|
||||
|
||||
foo.data[0].1.set(Some(&foo.data[1]));
|
||||
foo.data[1].1.set(Some(&foo.data[0]));
|
||||
}
|
||||
|
48
src/test/run-pass/issue28498-ugeh-with-lifetime-param.rs
Normal file
48
src/test/run-pass/issue28498-ugeh-with-lifetime-param.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
// Demonstrate the use of the unguarded escape hatch with a lifetime param
|
||||
// to assert that destructor will not access any dead data.
|
||||
//
|
||||
// Compare with compile-fail/issue28498-reject-lifetime-param.rs
|
||||
|
||||
#![feature(dropck_parametricity)]
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ScribbleOnDrop(String);
|
||||
|
||||
impl Drop for ScribbleOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0 = format!("DROPPED");
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo<'a>(u32, &'a ScribbleOnDrop);
|
||||
|
||||
impl<'a> Drop for Foo<'a> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
// Use of `unsafe_destructor_blind_to_params` is sound,
|
||||
// because destructor never accesses `self.1`.
|
||||
println!("Dropping Foo({}, _)", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (last_dropped, foo0);
|
||||
let (foo1, first_dropped);
|
||||
|
||||
last_dropped = ScribbleOnDrop(format!("last"));
|
||||
first_dropped = ScribbleOnDrop(format!("first"));
|
||||
foo0 = Foo(0, &last_dropped);
|
||||
foo1 = Foo(1, &first_dropped);
|
||||
|
||||
println!("foo0.1: {:?} foo1.1: {:?}", foo0.1, foo1.1);
|
||||
}
|
56
src/test/run-pass/issue28498-ugeh-with-passed-to-fn.rs
Normal file
56
src/test/run-pass/issue28498-ugeh-with-passed-to-fn.rs
Normal file
@ -0,0 +1,56 @@
|
||||
// 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.
|
||||
|
||||
// Demonstrate the use of the unguarded escape hatch with a type param in negative position
|
||||
// to assert that destructor will not access any dead data.
|
||||
//
|
||||
// Compare with compile-fail/issue28498-reject-lifetime-param.rs
|
||||
|
||||
// Demonstrate that a type param in negative position causes dropck to reject code
|
||||
// that might indirectly access previously dropped value.
|
||||
//
|
||||
// Compare with run-pass/issue28498-ugeh-with-passed-to-fn.rs
|
||||
|
||||
#![feature(dropck_parametricity)]
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ScribbleOnDrop(String);
|
||||
|
||||
impl Drop for ScribbleOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0 = format!("DROPPED");
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo<T>(u32, T, Box<for <'r> fn(&'r T) -> String>);
|
||||
|
||||
impl<T> Drop for Foo<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
// Use of `unsafe_destructor_blind_to_params` is sound,
|
||||
// because destructor never passes a `self.1` to the callback
|
||||
// (in `self.2`) despite having it available.
|
||||
println!("Dropping Foo({}, _)", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn callback(s: & &ScribbleOnDrop) -> String { format!("{:?}", s) }
|
||||
|
||||
fn main() {
|
||||
let (last_dropped, foo0);
|
||||
let (foo1, first_dropped);
|
||||
|
||||
last_dropped = ScribbleOnDrop(format!("last"));
|
||||
first_dropped = ScribbleOnDrop(format!("first"));
|
||||
foo0 = Foo(0, &last_dropped, Box::new(callback));
|
||||
foo1 = Foo(1, &first_dropped, Box::new(callback));
|
||||
|
||||
println!("foo0.1: {:?} foo1.1: {:?}", foo0.1, foo1.1);
|
||||
}
|
51
src/test/run-pass/issue28498-ugeh-with-trait-bound.rs
Normal file
51
src/test/run-pass/issue28498-ugeh-with-trait-bound.rs
Normal file
@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
// Demonstrate the use of the unguarded escape hatch with a trait bound
|
||||
// to assert that destructor will not access any dead data.
|
||||
//
|
||||
// Compare with compile-fail/issue28498-reject-trait-bound.rs
|
||||
|
||||
#![feature(dropck_parametricity)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ScribbleOnDrop(String);
|
||||
|
||||
impl Drop for ScribbleOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0 = format!("DROPPED");
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo<T:fmt::Debug>(u32, T);
|
||||
|
||||
impl<T:fmt::Debug> Drop for Foo<T> {
|
||||
#[unsafe_destructor_blind_to_params]
|
||||
fn drop(&mut self) {
|
||||
// Use of `unsafe_destructor_blind_to_params` is sound,
|
||||
// because destructor never accesses the `Debug::fmt` method
|
||||
// of `T`, despite having it available.
|
||||
println!("Dropping Foo({}, _)", self.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (last_dropped, foo0);
|
||||
let (foo1, first_dropped);
|
||||
|
||||
last_dropped = ScribbleOnDrop(format!("last"));
|
||||
first_dropped = ScribbleOnDrop(format!("first"));
|
||||
foo0 = Foo(0, &last_dropped);
|
||||
foo1 = Foo(1, &first_dropped);
|
||||
|
||||
println!("foo0.1: {:?} foo1.1: {:?}", foo0.1, foo1.1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user