Auto merge of #21972 - pnkfelix:new-dtor-semantics-6, r=nikomatsakis
This is a resurrection and heavy revision/expansion of a PR that pcwalton did to resolve #8861. The most relevant, user-visible semantic change is this: #[unsafe_destructor] is gone. Instead, if a type expression for some value has a destructor, then any lifetimes referenced within that type expression must strictly outlive the scope of the value. See discussion on https://github.com/rust-lang/rfcs/pull/769
This commit is contained in:
commit
e29f420255
|
@ -42,6 +42,7 @@ use std::cell::{Cell, RefCell};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::intrinsics::{TyDesc, get_tydesc};
|
use std::intrinsics::{TyDesc, get_tydesc};
|
||||||
use std::intrinsics;
|
use std::intrinsics;
|
||||||
|
use std::marker;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::num::{Int, UnsignedInt};
|
use std::num::{Int, UnsignedInt};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
@ -88,27 +89,29 @@ impl Chunk {
|
||||||
/// than objects without destructors. This reduces overhead when initializing
|
/// than objects without destructors. This reduces overhead when initializing
|
||||||
/// plain-old-data (`Copy` types) and means we don't need to waste time running
|
/// plain-old-data (`Copy` types) and means we don't need to waste time running
|
||||||
/// their destructors.
|
/// their destructors.
|
||||||
pub struct Arena {
|
pub struct Arena<'longer_than_self> {
|
||||||
// The head is separated out from the list as a unbenchmarked
|
// The head is separated out from the list as a unbenchmarked
|
||||||
// microoptimization, to avoid needing to case on the list to access the
|
// microoptimization, to avoid needing to case on the list to access the
|
||||||
// head.
|
// head.
|
||||||
head: RefCell<Chunk>,
|
head: RefCell<Chunk>,
|
||||||
copy_head: RefCell<Chunk>,
|
copy_head: RefCell<Chunk>,
|
||||||
chunks: RefCell<Vec<Chunk>>,
|
chunks: RefCell<Vec<Chunk>>,
|
||||||
|
_invariant: marker::InvariantLifetime<'longer_than_self>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arena {
|
impl<'a> Arena<'a> {
|
||||||
/// Allocates a new Arena with 32 bytes preallocated.
|
/// Allocates a new Arena with 32 bytes preallocated.
|
||||||
pub fn new() -> Arena {
|
pub fn new() -> Arena<'a> {
|
||||||
Arena::new_with_size(32)
|
Arena::new_with_size(32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a new Arena with `initial_size` bytes preallocated.
|
/// Allocates a new Arena with `initial_size` bytes preallocated.
|
||||||
pub fn new_with_size(initial_size: usize) -> Arena {
|
pub fn new_with_size(initial_size: usize) -> Arena<'a> {
|
||||||
Arena {
|
Arena {
|
||||||
head: RefCell::new(chunk(initial_size, false)),
|
head: RefCell::new(chunk(initial_size, false)),
|
||||||
copy_head: RefCell::new(chunk(initial_size, true)),
|
copy_head: RefCell::new(chunk(initial_size, true)),
|
||||||
chunks: RefCell::new(Vec::new()),
|
chunks: RefCell::new(Vec::new()),
|
||||||
|
_invariant: marker::InvariantLifetime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +125,7 @@ fn chunk(size: usize, is_copy: bool) -> Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
impl Drop for Arena {
|
impl<'longer_than_self> Drop for Arena<'longer_than_self> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
destroy_chunk(&*self.head.borrow());
|
destroy_chunk(&*self.head.borrow());
|
||||||
|
@ -180,7 +183,7 @@ fn un_bitpack_tydesc_ptr(p: usize) -> (*const TyDesc, bool) {
|
||||||
((p & !1) as *const TyDesc, p & 1 == 1)
|
((p & !1) as *const TyDesc, p & 1 == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arena {
|
impl<'longer_than_self> Arena<'longer_than_self> {
|
||||||
fn chunk_size(&self) -> usize {
|
fn chunk_size(&self) -> usize {
|
||||||
self.copy_head.borrow().capacity()
|
self.copy_head.borrow().capacity()
|
||||||
}
|
}
|
||||||
|
@ -293,7 +296,7 @@ impl Arena {
|
||||||
/// Allocates a new item in the arena, using `op` to initialize the value,
|
/// Allocates a new item in the arena, using `op` to initialize the value,
|
||||||
/// and returns a reference to it.
|
/// and returns a reference to it.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn alloc<T, F>(&self, op: F) -> &mut T where F: FnOnce() -> T {
|
pub fn alloc<T:'longer_than_self, F>(&self, op: F) -> &mut T where F: FnOnce() -> T {
|
||||||
unsafe {
|
unsafe {
|
||||||
if intrinsics::needs_drop::<T>() {
|
if intrinsics::needs_drop::<T>() {
|
||||||
self.alloc_noncopy(op)
|
self.alloc_noncopy(op)
|
||||||
|
@ -317,20 +320,6 @@ fn test_arena_destructors() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_arena_alloc_nested() {
|
|
||||||
struct Inner { value: usize }
|
|
||||||
struct Outer<'a> { inner: &'a Inner }
|
|
||||||
|
|
||||||
let arena = Arena::new();
|
|
||||||
|
|
||||||
let result = arena.alloc(|| Outer {
|
|
||||||
inner: arena.alloc(|| Inner { value: 10 })
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(result.inner.value, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_fail]
|
#[should_fail]
|
||||||
fn test_arena_destructors_fail() {
|
fn test_arena_destructors_fail() {
|
||||||
|
@ -365,6 +354,10 @@ pub struct TypedArena<T> {
|
||||||
|
|
||||||
/// A pointer to the first arena segment.
|
/// A pointer to the first arena segment.
|
||||||
first: RefCell<*mut TypedArenaChunk<T>>,
|
first: RefCell<*mut TypedArenaChunk<T>>,
|
||||||
|
|
||||||
|
/// Marker indicating that dropping the arena causes its owned
|
||||||
|
/// instances of `T` to be dropped.
|
||||||
|
_own: marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypedArenaChunk<T> {
|
struct TypedArenaChunk<T> {
|
||||||
|
@ -460,6 +453,7 @@ impl<T> TypedArena<T> {
|
||||||
ptr: Cell::new((*chunk).start() as *const T),
|
ptr: Cell::new((*chunk).start() as *const T),
|
||||||
end: Cell::new((*chunk).end() as *const T),
|
end: Cell::new((*chunk).end() as *const T),
|
||||||
first: RefCell::new(chunk),
|
first: RefCell::new(chunk),
|
||||||
|
_own: marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -523,6 +517,41 @@ mod tests {
|
||||||
z: i32,
|
z: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arena_alloc_nested() {
|
||||||
|
struct Inner { value: u8 }
|
||||||
|
struct Outer<'a> { inner: &'a Inner }
|
||||||
|
enum EI<'e> { I(Inner), O(Outer<'e>) }
|
||||||
|
|
||||||
|
struct Wrap<'a>(TypedArena<EI<'a>>);
|
||||||
|
|
||||||
|
impl<'a> Wrap<'a> {
|
||||||
|
fn alloc_inner<F:Fn() -> Inner>(&self, f: F) -> &Inner {
|
||||||
|
let r: &EI = self.0.alloc(EI::I(f()));
|
||||||
|
if let &EI::I(ref i) = r {
|
||||||
|
i
|
||||||
|
} else {
|
||||||
|
panic!("mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn alloc_outer<F:Fn() -> Outer<'a>>(&self, f: F) -> &Outer {
|
||||||
|
let r: &EI = self.0.alloc(EI::O(f()));
|
||||||
|
if let &EI::O(ref o) = r {
|
||||||
|
o
|
||||||
|
} else {
|
||||||
|
panic!("mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let arena = Wrap(TypedArena::new());
|
||||||
|
|
||||||
|
let result = arena.alloc_outer(|| Outer {
|
||||||
|
inner: arena.alloc_inner(|| Inner { value: 10 }) });
|
||||||
|
|
||||||
|
assert_eq!(result.inner.value, 10);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_copy() {
|
pub fn test_copy() {
|
||||||
let arena = TypedArena::new();
|
let arena = TypedArena::new();
|
||||||
|
|
|
@ -278,7 +278,7 @@ impl<T> Drop for RawItems<T> {
|
||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
impl<K, V> Drop for Node<K, V> {
|
impl<K, V> Drop for Node<K, V> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.keys.0.is_null() {
|
if self.keys.ptr.is_null() {
|
||||||
// We have already cleaned up this node.
|
// We have already cleaned up this node.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -292,7 +292,7 @@ impl<K, V> Drop for Node<K, V> {
|
||||||
self.destroy();
|
self.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.keys.0 = ptr::null_mut();
|
self.keys.ptr = ptr::null_mut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,18 +337,18 @@ impl<K, V> Node<K, V> {
|
||||||
unsafe fn destroy(&mut self) {
|
unsafe fn destroy(&mut self) {
|
||||||
let (alignment, size) =
|
let (alignment, size) =
|
||||||
calculate_allocation_generic::<K, V>(self.capacity(), self.is_leaf());
|
calculate_allocation_generic::<K, V>(self.capacity(), self.is_leaf());
|
||||||
heap::deallocate(self.keys.0 as *mut u8, size, alignment);
|
heap::deallocate(self.keys.ptr as *mut u8, size, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_slices<'a>(&'a self) -> (&'a [K], &'a [V]) {
|
pub fn as_slices<'a>(&'a self) -> (&'a [K], &'a [V]) {
|
||||||
unsafe {(
|
unsafe {(
|
||||||
mem::transmute(raw::Slice {
|
mem::transmute(raw::Slice {
|
||||||
data: self.keys.0,
|
data: self.keys.ptr,
|
||||||
len: self.len()
|
len: self.len()
|
||||||
}),
|
}),
|
||||||
mem::transmute(raw::Slice {
|
mem::transmute(raw::Slice {
|
||||||
data: self.vals.0,
|
data: self.vals.ptr,
|
||||||
len: self.len()
|
len: self.len()
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
|
@ -368,7 +368,7 @@ impl<K, V> Node<K, V> {
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
mem::transmute(raw::Slice {
|
mem::transmute(raw::Slice {
|
||||||
data: self.edges.0,
|
data: self.edges.ptr,
|
||||||
len: self.len() + 1
|
len: self.len() + 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -586,7 +586,7 @@ impl <K, V> Node<K, V> {
|
||||||
|
|
||||||
/// If the node has any children
|
/// If the node has any children
|
||||||
pub fn is_leaf(&self) -> bool {
|
pub fn is_leaf(&self) -> bool {
|
||||||
self.edges.0.is_null()
|
self.edges.ptr.is_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if the node has too few elements
|
/// if the node has too few elements
|
||||||
|
@ -1064,7 +1064,7 @@ impl<K, V> Node<K, V> {
|
||||||
vals: RawItems::from_slice(self.vals()),
|
vals: RawItems::from_slice(self.vals()),
|
||||||
edges: RawItems::from_slice(self.edges()),
|
edges: RawItems::from_slice(self.edges()),
|
||||||
|
|
||||||
ptr: self.keys.0 as *mut u8,
|
ptr: self.keys.ptr as *mut u8,
|
||||||
capacity: self.capacity(),
|
capacity: self.capacity(),
|
||||||
is_leaf: self.is_leaf()
|
is_leaf: self.is_leaf()
|
||||||
},
|
},
|
||||||
|
|
|
@ -57,7 +57,7 @@ use core::default::Default;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::hash::{self, Hash};
|
use core::hash::{self, Hash};
|
||||||
use core::iter::{repeat, FromIterator, IntoIterator};
|
use core::iter::{repeat, FromIterator, IntoIterator};
|
||||||
use core::marker::{ContravariantLifetime, InvariantType};
|
use core::marker::{self, ContravariantLifetime, InvariantType};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::nonzero::NonZero;
|
use core::nonzero::NonZero;
|
||||||
use core::num::{Int, UnsignedInt};
|
use core::num::{Int, UnsignedInt};
|
||||||
|
@ -140,6 +140,7 @@ pub struct Vec<T> {
|
||||||
ptr: NonZero<*mut T>,
|
ptr: NonZero<*mut T>,
|
||||||
len: usize,
|
len: usize,
|
||||||
cap: usize,
|
cap: usize,
|
||||||
|
_own: marker::PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for Vec<T> { }
|
unsafe impl<T: Send> Send for Vec<T> { }
|
||||||
|
@ -166,7 +167,7 @@ impl<T> Vec<T> {
|
||||||
// non-null value which is fine since we never call deallocate on the ptr
|
// non-null value which is fine since we never call deallocate on the ptr
|
||||||
// if cap is 0. The reason for this is because the pointer of a slice
|
// if cap is 0. The reason for this is because the pointer of a slice
|
||||||
// being NULL would break the null pointer optimization for enums.
|
// being NULL would break the null pointer optimization for enums.
|
||||||
Vec { ptr: unsafe { NonZero::new(EMPTY as *mut T) }, len: 0, cap: 0 }
|
unsafe { Vec::from_raw_parts(EMPTY as *mut T, 0, 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new, empty `Vec<T>` with the specified capacity.
|
/// Constructs a new, empty `Vec<T>` with the specified capacity.
|
||||||
|
@ -198,7 +199,7 @@ impl<T> Vec<T> {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn with_capacity(capacity: usize) -> Vec<T> {
|
pub fn with_capacity(capacity: usize) -> Vec<T> {
|
||||||
if mem::size_of::<T>() == 0 {
|
if mem::size_of::<T>() == 0 {
|
||||||
Vec { ptr: unsafe { NonZero::new(EMPTY as *mut T) }, len: 0, cap: usize::MAX }
|
unsafe { Vec::from_raw_parts(EMPTY as *mut T, 0, usize::MAX) }
|
||||||
} else if capacity == 0 {
|
} else if capacity == 0 {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
|
@ -206,7 +207,7 @@ impl<T> Vec<T> {
|
||||||
.expect("capacity overflow");
|
.expect("capacity overflow");
|
||||||
let ptr = unsafe { allocate(size, mem::min_align_of::<T>()) };
|
let ptr = unsafe { allocate(size, mem::min_align_of::<T>()) };
|
||||||
if ptr.is_null() { ::alloc::oom() }
|
if ptr.is_null() { ::alloc::oom() }
|
||||||
Vec { ptr: unsafe { NonZero::new(ptr as *mut T) }, len: 0, cap: capacity }
|
unsafe { Vec::from_raw_parts(ptr as *mut T, 0, capacity) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +248,12 @@ impl<T> Vec<T> {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub unsafe fn from_raw_parts(ptr: *mut T, length: usize,
|
pub unsafe fn from_raw_parts(ptr: *mut T, length: usize,
|
||||||
capacity: usize) -> Vec<T> {
|
capacity: usize) -> Vec<T> {
|
||||||
Vec { ptr: NonZero::new(ptr), len: length, cap: capacity }
|
Vec {
|
||||||
|
ptr: NonZero::new(ptr),
|
||||||
|
len: length,
|
||||||
|
cap: capacity,
|
||||||
|
_own: marker::PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a vector by copying the elements from a raw pointer.
|
/// Creates a vector by copying the elements from a raw pointer.
|
||||||
|
@ -1626,7 +1632,7 @@ impl<T> IntoIter<T> {
|
||||||
for _x in self.by_ref() { }
|
for _x in self.by_ref() { }
|
||||||
let IntoIter { allocation, cap, ptr: _ptr, end: _end } = self;
|
let IntoIter { allocation, cap, ptr: _ptr, end: _end } = self;
|
||||||
mem::forget(self);
|
mem::forget(self);
|
||||||
Vec { ptr: NonZero::new(allocation), cap: cap, len: 0 }
|
Vec::from_raw_parts(allocation, 0, cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,24 @@ pub unsafe trait Sync {
|
||||||
// Empty
|
// Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A marker type that indicates to the compiler that the instances
|
||||||
|
/// of the type itself owns instances of the type parameter `T`.
|
||||||
|
///
|
||||||
|
/// This is used to indicate that one or more instances of the type
|
||||||
|
/// `T` could be dropped when instances of the type itself is dropped,
|
||||||
|
/// though that may not be apparent from the other structure of the
|
||||||
|
/// type itself. For example, the type may hold a `*mut T`, which the
|
||||||
|
/// compiler does not automatically treat as owned.
|
||||||
|
#[unstable(feature = "core",
|
||||||
|
reason = "Newly added to deal with scoping and destructor changes")]
|
||||||
|
#[lang="phantom_data"]
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct PhantomData<T: ?Sized>;
|
||||||
|
|
||||||
|
impl<T: ?Sized> Copy for PhantomData<T> {}
|
||||||
|
impl<T: ?Sized> Clone for PhantomData<T> {
|
||||||
|
fn clone(&self) -> PhantomData<T> { *self }
|
||||||
|
}
|
||||||
|
|
||||||
/// A marker type whose type parameter `T` is considered to be
|
/// A marker type whose type parameter `T` is considered to be
|
||||||
/// covariant with respect to the type itself. This is (typically)
|
/// covariant with respect to the type itself. This is (typically)
|
||||||
|
|
|
@ -92,7 +92,7 @@ use mem;
|
||||||
use clone::Clone;
|
use clone::Clone;
|
||||||
use intrinsics;
|
use intrinsics;
|
||||||
use option::Option::{self, Some, None};
|
use option::Option::{self, Some, None};
|
||||||
use marker::{Send, Sized, Sync};
|
use marker::{self, Send, Sized, Sync};
|
||||||
|
|
||||||
use cmp::{PartialEq, Eq, Ord, PartialOrd};
|
use cmp::{PartialEq, Eq, Ord, PartialOrd};
|
||||||
use cmp::Ordering::{self, Less, Equal, Greater};
|
use cmp::Ordering::{self, Less, Equal, Greater};
|
||||||
|
@ -522,7 +522,11 @@ impl<T> PartialOrd for *mut T {
|
||||||
/// Useful for building abstractions like `Vec<T>` or `Box<T>`, which
|
/// Useful for building abstractions like `Vec<T>` or `Box<T>`, which
|
||||||
/// internally use raw pointers to manage the memory that they own.
|
/// internally use raw pointers to manage the memory that they own.
|
||||||
#[unstable(feature = "core", reason = "recently added to this module")]
|
#[unstable(feature = "core", reason = "recently added to this module")]
|
||||||
pub struct Unique<T: ?Sized>(pub *mut T);
|
pub struct Unique<T: ?Sized> {
|
||||||
|
/// The wrapped `*mut T`.
|
||||||
|
pub ptr: *mut T,
|
||||||
|
_own: marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
/// `Unique` pointers are `Send` if `T` is `Send` because the data they
|
/// `Unique` pointers are `Send` if `T` is `Send` because the data they
|
||||||
/// reference is unaliased. Note that this aliasing invariant is
|
/// reference is unaliased. Note that this aliasing invariant is
|
||||||
|
@ -550,6 +554,13 @@ impl<T> Unique<T> {
|
||||||
#[unstable(feature = "core",
|
#[unstable(feature = "core",
|
||||||
reason = "recently added to this module")]
|
reason = "recently added to this module")]
|
||||||
pub unsafe fn offset(self, offset: int) -> *mut T {
|
pub unsafe fn offset(self, offset: int) -> *mut T {
|
||||||
self.0.offset(offset)
|
self.ptr.offset(offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a `Unique` wrapped around `ptr`, taking ownership of the
|
||||||
|
/// data referenced by `ptr`.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn Unique<T: ?Sized>(ptr: *mut T) -> Unique<T> {
|
||||||
|
Unique { ptr: ptr, _own: marker::PhantomData }
|
||||||
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ fn test_set_memory() {
|
||||||
fn test_unsized_unique() {
|
fn test_unsized_unique() {
|
||||||
let xs: &mut [_] = &mut [1, 2, 3];
|
let xs: &mut [_] = &mut [1, 2, 3];
|
||||||
let ptr = Unique(xs as *mut [_]);
|
let ptr = Unique(xs as *mut [_]);
|
||||||
let ys = unsafe { &mut *ptr.0 };
|
let ys = unsafe { &mut *ptr.ptr };
|
||||||
let zs: &mut [_] = &mut [1, 2, 3];
|
let zs: &mut [_] = &mut [1, 2, 3];
|
||||||
assert!(ys == zs);
|
assert!(ys == zs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,13 +45,13 @@ pub struct Bytes {
|
||||||
impl Deref for Bytes {
|
impl Deref for Bytes {
|
||||||
type Target = [u8];
|
type Target = [u8];
|
||||||
fn deref(&self) -> &[u8] {
|
fn deref(&self) -> &[u8] {
|
||||||
unsafe { slice::from_raw_parts_mut(self.ptr.0, self.len) }
|
unsafe { slice::from_raw_parts_mut(self.ptr.ptr, self.len) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Bytes {
|
impl Drop for Bytes {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { libc::free(self.ptr.0 as *mut _); }
|
unsafe { libc::free(self.ptr.ptr as *mut _); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,7 @@ fn parse_region_<F>(st: &mut PState, conv: &mut F) -> ty::Region where
|
||||||
}
|
}
|
||||||
'f' => {
|
'f' => {
|
||||||
assert_eq!(next(st), '[');
|
assert_eq!(next(st), '[');
|
||||||
let scope = parse_scope(st);
|
let scope = parse_destruction_scope_data(st);
|
||||||
assert_eq!(next(st), '|');
|
assert_eq!(next(st), '|');
|
||||||
let br = parse_bound_region_(st, conv);
|
let br = parse_bound_region_(st, conv);
|
||||||
assert_eq!(next(st), ']');
|
assert_eq!(next(st), ']');
|
||||||
|
@ -377,6 +377,10 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
|
||||||
let node_id = parse_uint(st) as ast::NodeId;
|
let node_id = parse_uint(st) as ast::NodeId;
|
||||||
region::CodeExtent::Misc(node_id)
|
region::CodeExtent::Misc(node_id)
|
||||||
}
|
}
|
||||||
|
'D' => {
|
||||||
|
let node_id = parse_uint(st) as ast::NodeId;
|
||||||
|
region::CodeExtent::DestructionScope(node_id)
|
||||||
|
}
|
||||||
'B' => {
|
'B' => {
|
||||||
let node_id = parse_uint(st) as ast::NodeId;
|
let node_id = parse_uint(st) as ast::NodeId;
|
||||||
let first_stmt_index = parse_uint(st);
|
let first_stmt_index = parse_uint(st);
|
||||||
|
@ -389,6 +393,11 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_destruction_scope_data(st: &mut PState) -> region::DestructionScopeData {
|
||||||
|
let node_id = parse_uint(st) as ast::NodeId;
|
||||||
|
region::DestructionScopeData::new(node_id)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_opt<'a, 'tcx, T, F>(st: &mut PState<'a, 'tcx>, f: F) -> Option<T> where
|
fn parse_opt<'a, 'tcx, T, F>(st: &mut PState<'a, 'tcx>, f: F) -> Option<T> where
|
||||||
F: FnOnce(&mut PState<'a, 'tcx>) -> T,
|
F: FnOnce(&mut PState<'a, 'tcx>) -> T,
|
||||||
{
|
{
|
||||||
|
|
|
@ -251,7 +251,7 @@ pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) {
|
||||||
}
|
}
|
||||||
ty::ReFree(ref fr) => {
|
ty::ReFree(ref fr) => {
|
||||||
mywrite!(w, "f[");
|
mywrite!(w, "f[");
|
||||||
enc_scope(w, cx, fr.scope);
|
enc_destruction_scope_data(w, fr.scope);
|
||||||
mywrite!(w, "|");
|
mywrite!(w, "|");
|
||||||
enc_bound_region(w, cx, fr.bound_region);
|
enc_bound_region(w, cx, fr.bound_region);
|
||||||
mywrite!(w, "]");
|
mywrite!(w, "]");
|
||||||
|
@ -279,9 +279,15 @@ fn enc_scope(w: &mut SeekableMemWriter, _cx: &ctxt, scope: region::CodeExtent) {
|
||||||
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id),
|
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id),
|
||||||
region::CodeExtent::Remainder(region::BlockRemainder {
|
region::CodeExtent::Remainder(region::BlockRemainder {
|
||||||
block: b, first_statement_index: i }) => mywrite!(w, "B{}{}", b, i),
|
block: b, first_statement_index: i }) => mywrite!(w, "B{}{}", b, i),
|
||||||
|
region::CodeExtent::DestructionScope(node_id) => mywrite!(w, "D{}", node_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enc_destruction_scope_data(w: &mut SeekableMemWriter,
|
||||||
|
d: region::DestructionScopeData) {
|
||||||
|
mywrite!(w, "{}", d.node_id);
|
||||||
|
}
|
||||||
|
|
||||||
fn enc_bound_region(w: &mut SeekableMemWriter, cx: &ctxt, br: ty::BoundRegion) {
|
fn enc_bound_region(w: &mut SeekableMemWriter, cx: &ctxt, br: ty::BoundRegion) {
|
||||||
match br {
|
match br {
|
||||||
ty::BrAnon(idx) => {
|
ty::BrAnon(idx) => {
|
||||||
|
|
|
@ -499,6 +499,12 @@ impl tr for region::CodeExtent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl tr for region::DestructionScopeData {
|
||||||
|
fn tr(&self, dcx: &DecodeContext) -> region::DestructionScopeData {
|
||||||
|
region::DestructionScopeData { node_id: dcx.tr_id(self.node_id) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl tr for ty::BoundRegion {
|
impl tr for ty::BoundRegion {
|
||||||
fn tr(&self, dcx: &DecodeContext) -> ty::BoundRegion {
|
fn tr(&self, dcx: &DecodeContext) -> ty::BoundRegion {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
@ -242,7 +242,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SubSupConflict(var_origin, _, sub_r, _, sup_r) => {
|
SubSupConflict(var_origin, _, sub_r, _, sup_r) => {
|
||||||
debug!("processing SubSupConflict");
|
debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub_r, sup_r);
|
||||||
match free_regions_from_same_fn(self.tcx, sub_r, sup_r) {
|
match free_regions_from_same_fn(self.tcx, sub_r, sup_r) {
|
||||||
Some(ref same_frs) => {
|
Some(ref same_frs) => {
|
||||||
var_origins.push(var_origin);
|
var_origins.push(var_origin);
|
||||||
|
@ -304,7 +304,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
assert!(fr1.scope == fr2.scope);
|
assert!(fr1.scope == fr2.scope);
|
||||||
(fr1.scope.node_id(), fr1, fr2)
|
(fr1.scope.node_id, fr1, fr2)
|
||||||
},
|
},
|
||||||
_ => return None
|
_ => return None
|
||||||
};
|
};
|
||||||
|
@ -709,6 +709,23 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
sup,
|
sup,
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
infer::SafeDestructor(span) => {
|
||||||
|
self.tcx.sess.span_err(
|
||||||
|
span,
|
||||||
|
"unsafe use of destructor: destructor might be called \
|
||||||
|
while references are dead");
|
||||||
|
// FIXME (22171): terms "super/subregion" are suboptimal
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
"superregion: ",
|
||||||
|
sup,
|
||||||
|
"");
|
||||||
|
note_and_explain_region(
|
||||||
|
self.tcx,
|
||||||
|
"subregion: ",
|
||||||
|
sub,
|
||||||
|
"");
|
||||||
|
}
|
||||||
infer::BindingTypeIsNotValidAtDecl(span) => {
|
infer::BindingTypeIsNotValidAtDecl(span) => {
|
||||||
self.tcx.sess.span_err(
|
self.tcx.sess.span_err(
|
||||||
span,
|
span,
|
||||||
|
@ -1629,6 +1646,12 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
&format!("...so that the declared lifetime parameter bounds \
|
&format!("...so that the declared lifetime parameter bounds \
|
||||||
are satisfied")[]);
|
are satisfied")[]);
|
||||||
}
|
}
|
||||||
|
infer::SafeDestructor(span) => {
|
||||||
|
self.tcx.sess.span_note(
|
||||||
|
span,
|
||||||
|
"...so that references are valid when the destructor \
|
||||||
|
runs")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,6 +215,9 @@ pub enum SubregionOrigin<'tcx> {
|
||||||
|
|
||||||
// An auto-borrow that does not enclose the expr where it occurs
|
// An auto-borrow that does not enclose the expr where it occurs
|
||||||
AutoBorrow(Span),
|
AutoBorrow(Span),
|
||||||
|
|
||||||
|
// Region constraint arriving from destructor safety
|
||||||
|
SafeDestructor(Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Times when we replace late-bound regions with variables:
|
/// Times when we replace late-bound regions with variables:
|
||||||
|
@ -1197,6 +1200,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
|
||||||
CallReturn(a) => a,
|
CallReturn(a) => a,
|
||||||
AddrOf(a) => a,
|
AddrOf(a) => a,
|
||||||
AutoBorrow(a) => a,
|
AutoBorrow(a) => a,
|
||||||
|
SafeDestructor(a) => a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1259,6 +1263,7 @@ impl<'tcx> Repr<'tcx> for SubregionOrigin<'tcx> {
|
||||||
CallReturn(a) => format!("CallReturn({})", a.repr(tcx)),
|
CallReturn(a) => format!("CallReturn({})", a.repr(tcx)),
|
||||||
AddrOf(a) => format!("AddrOf({})", a.repr(tcx)),
|
AddrOf(a) => format!("AddrOf({})", a.repr(tcx)),
|
||||||
AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)),
|
AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)),
|
||||||
|
SafeDestructor(a) => format!("SafeDestructor({})", a.repr(tcx)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -760,11 +760,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||||
// A "free" region can be interpreted as "some region
|
// A "free" region can be interpreted as "some region
|
||||||
// at least as big as the block fr.scope_id". So, we can
|
// at least as big as the block fr.scope_id". So, we can
|
||||||
// reasonably compare free regions and scopes:
|
// reasonably compare free regions and scopes:
|
||||||
match self.tcx.region_maps.nearest_common_ancestor(fr.scope, s_id) {
|
let fr_scope = fr.scope.to_code_extent();
|
||||||
|
match self.tcx.region_maps.nearest_common_ancestor(fr_scope, s_id) {
|
||||||
// if the free region's scope `fr.scope_id` is bigger than
|
// if the free region's scope `fr.scope_id` is bigger than
|
||||||
// the scope region `s_id`, then the LUB is the free
|
// the scope region `s_id`, then the LUB is the free
|
||||||
// region itself:
|
// region itself:
|
||||||
Some(r_id) if r_id == fr.scope => f,
|
Some(r_id) if r_id == fr_scope => f,
|
||||||
|
|
||||||
// otherwise, we don't know what the free region is,
|
// otherwise, we don't know what the free region is,
|
||||||
// so we must conservatively say the LUB is static:
|
// so we must conservatively say the LUB is static:
|
||||||
|
@ -865,8 +866,9 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||||
// than the scope `s_id`, then we can say that the GLB
|
// than the scope `s_id`, then we can say that the GLB
|
||||||
// is the scope `s_id`. Otherwise, as we do not know
|
// is the scope `s_id`. Otherwise, as we do not know
|
||||||
// big the free region is precisely, the GLB is undefined.
|
// big the free region is precisely, the GLB is undefined.
|
||||||
match self.tcx.region_maps.nearest_common_ancestor(fr.scope, s_id) {
|
let fr_scope = fr.scope.to_code_extent();
|
||||||
Some(r_id) if r_id == fr.scope => Ok(s),
|
match self.tcx.region_maps.nearest_common_ancestor(fr_scope, s_id) {
|
||||||
|
Some(r_id) if r_id == fr_scope => Ok(s),
|
||||||
_ => Err(ty::terr_regions_no_overlap(b, a))
|
_ => Err(ty::terr_regions_no_overlap(b, a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -915,7 +917,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||||
Ok(ty::ReFree(*b))
|
Ok(ty::ReFree(*b))
|
||||||
} else {
|
} else {
|
||||||
this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b),
|
this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b),
|
||||||
a.scope, b.scope)
|
a.scope.to_code_extent(),
|
||||||
|
b.scope.to_code_extent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1396,6 +1399,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
|
||||||
for upper_bound in &upper_bounds {
|
for upper_bound in &upper_bounds {
|
||||||
if !self.is_subregion_of(lower_bound.region,
|
if !self.is_subregion_of(lower_bound.region,
|
||||||
upper_bound.region) {
|
upper_bound.region) {
|
||||||
|
debug!("pushing SubSupConflict sub: {:?} sup: {:?}",
|
||||||
|
lower_bound.region, upper_bound.region);
|
||||||
errors.push(SubSupConflict(
|
errors.push(SubSupConflict(
|
||||||
(*self.var_origins.borrow())[node_idx.index as uint].clone(),
|
(*self.var_origins.borrow())[node_idx.index as uint].clone(),
|
||||||
lower_bound.origin.clone(),
|
lower_bound.origin.clone(),
|
||||||
|
|
|
@ -312,6 +312,8 @@ lets_do_this! {
|
||||||
ExchangeHeapLangItem, "exchange_heap", exchange_heap;
|
ExchangeHeapLangItem, "exchange_heap", exchange_heap;
|
||||||
OwnedBoxLangItem, "owned_box", owned_box;
|
OwnedBoxLangItem, "owned_box", owned_box;
|
||||||
|
|
||||||
|
PhantomDataItem, "phantom_data", phantom_data;
|
||||||
|
|
||||||
CovariantTypeItem, "covariant_type", covariant_type;
|
CovariantTypeItem, "covariant_type", covariant_type;
|
||||||
ContravariantTypeItem, "contravariant_type", contravariant_type;
|
ContravariantTypeItem, "contravariant_type", contravariant_type;
|
||||||
InvariantTypeItem, "invariant_type", invariant_type;
|
InvariantTypeItem, "invariant_type", invariant_type;
|
||||||
|
|
|
@ -112,7 +112,7 @@ use self::VarKind::*;
|
||||||
use middle::def::*;
|
use middle::def::*;
|
||||||
use middle::mem_categorization::Typer;
|
use middle::mem_categorization::Typer;
|
||||||
use middle::pat_util;
|
use middle::pat_util;
|
||||||
use middle::region::CodeExtent;
|
use middle::region;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::ty::ClosureTyper;
|
use middle::ty::ClosureTyper;
|
||||||
use lint;
|
use lint;
|
||||||
|
@ -1514,7 +1514,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||||
let fn_ret =
|
let fn_ret =
|
||||||
ty::liberate_late_bound_regions(
|
ty::liberate_late_bound_regions(
|
||||||
self.ir.tcx,
|
self.ir.tcx,
|
||||||
CodeExtent::from_node_id(body.id),
|
region::DestructionScopeData::new(body.id),
|
||||||
&self.fn_ret(id));
|
&self.fn_ret(id));
|
||||||
|
|
||||||
match fn_ret {
|
match fn_ret {
|
||||||
|
|
|
@ -760,7 +760,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||||
|
|
||||||
// Region of environment pointer
|
// Region of environment pointer
|
||||||
let env_region = ty::ReFree(ty::FreeRegion {
|
let env_region = ty::ReFree(ty::FreeRegion {
|
||||||
scope: region::CodeExtent::from_node_id(fn_body_id),
|
// The environment of a closure is guaranteed to
|
||||||
|
// outlive any bindings introduced in the body of the
|
||||||
|
// closure itself.
|
||||||
|
scope: region::DestructionScopeData::new(fn_body_id),
|
||||||
bound_region: ty::BrEnv
|
bound_region: ty::BrEnv
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,66 @@ use syntax::{ast, visit};
|
||||||
use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
|
use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
|
||||||
use syntax::ast_util::{stmt_id};
|
use syntax::ast_util::{stmt_id};
|
||||||
use syntax::ast_map;
|
use syntax::ast_map;
|
||||||
|
use syntax::ptr::P;
|
||||||
use syntax::visit::{Visitor, FnKind};
|
use syntax::visit::{Visitor, FnKind};
|
||||||
|
|
||||||
/// CodeExtent represents a statically-describable extent that can be
|
/// CodeExtent represents a statically-describable extent that can be
|
||||||
/// used to bound the lifetime/region for values.
|
/// used to bound the lifetime/region for values.
|
||||||
///
|
///
|
||||||
|
/// `Misc(node_id)`: Any AST node that has any extent at all has the
|
||||||
|
/// `Misc(node_id)` extent. Other variants represent special cases not
|
||||||
|
/// immediately derivable from the abstract syntax tree structure.
|
||||||
|
///
|
||||||
|
/// `DestructionScope(node_id)` represents the extent of destructors
|
||||||
|
/// implicitly-attached to `node_id` that run immediately after the
|
||||||
|
/// expression for `node_id` itself. Not every AST node carries a
|
||||||
|
/// `DestructionScope`, but those that are `terminating_scopes` do;
|
||||||
|
/// see discussion with `RegionMaps`.
|
||||||
|
///
|
||||||
|
/// `Remainder(BlockRemainder { block, statement_index })` represents
|
||||||
|
/// the extent of user code running immediately after the initializer
|
||||||
|
/// expression for the indexed statement, until the end of the block.
|
||||||
|
///
|
||||||
|
/// So: the following code can be broken down into the extents beneath:
|
||||||
|
/// ```
|
||||||
|
/// let a = f().g( 'b: { let x = d(); let y = d(); x.h(y) } ) ;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// +-+ (D12.)
|
||||||
|
/// +-+ (D11.)
|
||||||
|
/// +---------+ (R10.)
|
||||||
|
/// +-+ (D9.)
|
||||||
|
/// +----------+ (M8.)
|
||||||
|
/// +----------------------+ (R7.)
|
||||||
|
/// +-+ (D6.)
|
||||||
|
/// +----------+ (M5.)
|
||||||
|
/// +-----------------------------------+ (M4.)
|
||||||
|
/// +--------------------------------------------------+ (M3.)
|
||||||
|
/// +--+ (M2.)
|
||||||
|
/// +-----------------------------------------------------------+ (M1.)
|
||||||
|
///
|
||||||
|
/// (M1.): Misc extent of the whole `let a = ...;` statement.
|
||||||
|
/// (M2.): Misc extent of the `f()` expression.
|
||||||
|
/// (M3.): Misc extent of the `f().g(..)` expression.
|
||||||
|
/// (M4.): Misc extent of the block labelled `'b:`.
|
||||||
|
/// (M5.): Misc extent of the `let x = d();` statement
|
||||||
|
/// (D6.): DestructionScope for temporaries created during M5.
|
||||||
|
/// (R7.): Remainder extent for block `'b:`, stmt 0 (let x = ...).
|
||||||
|
/// (M8.): Misc Extent of the `let y = d();` statement.
|
||||||
|
/// (D9.): DestructionScope for temporaries created during M8.
|
||||||
|
/// (R10.): Remainder extent for block `'b:`, stmt 1 (let y = ...).
|
||||||
|
/// (D11.): DestructionScope for temporaries and bindings from block `'b:`.
|
||||||
|
/// (D12.): DestructionScope for temporaries created during M1 (e.g. f()).
|
||||||
|
///
|
||||||
|
/// Note that while the above picture shows the destruction scopes
|
||||||
|
/// as following their corresponding misc extents, in the internal
|
||||||
|
/// data structures of the compiler the destruction scopes are
|
||||||
|
/// represented as enclosing parents. This is sound because we use the
|
||||||
|
/// enclosing parent relationship just to ensure that referenced
|
||||||
|
/// values live long enough; phrased another way, the starting point
|
||||||
|
/// of each range is not really the important thing in the above
|
||||||
|
/// picture, but rather the ending point.
|
||||||
|
///
|
||||||
/// FIXME (pnkfelix): This currently derives `PartialOrd` and `Ord` to
|
/// FIXME (pnkfelix): This currently derives `PartialOrd` and `Ord` to
|
||||||
/// placate the same deriving in `ty::FreeRegion`, but we may want to
|
/// placate the same deriving in `ty::FreeRegion`, but we may want to
|
||||||
/// actually attach a more meaningful ordering to scopes than the one
|
/// actually attach a more meaningful ordering to scopes than the one
|
||||||
|
@ -40,7 +95,24 @@ use syntax::visit::{Visitor, FnKind};
|
||||||
RustcDecodable, Debug, Copy)]
|
RustcDecodable, Debug, Copy)]
|
||||||
pub enum CodeExtent {
|
pub enum CodeExtent {
|
||||||
Misc(ast::NodeId),
|
Misc(ast::NodeId),
|
||||||
Remainder(BlockRemainder),
|
DestructionScope(ast::NodeId), // extent of destructors for temporaries of node-id
|
||||||
|
Remainder(BlockRemainder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// extent of destructors for temporaries of node-id
|
||||||
|
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable,
|
||||||
|
RustcDecodable, Debug, Copy)]
|
||||||
|
pub struct DestructionScopeData {
|
||||||
|
pub node_id: ast::NodeId
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DestructionScopeData {
|
||||||
|
pub fn new(node_id: ast::NodeId) -> DestructionScopeData {
|
||||||
|
DestructionScopeData { node_id: node_id }
|
||||||
|
}
|
||||||
|
pub fn to_code_extent(&self) -> CodeExtent {
|
||||||
|
CodeExtent::DestructionScope(self.node_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a subscope of `block` for a binding that is introduced
|
/// Represents a subscope of `block` for a binding that is introduced
|
||||||
|
@ -82,6 +154,7 @@ impl CodeExtent {
|
||||||
match *self {
|
match *self {
|
||||||
CodeExtent::Misc(node_id) => node_id,
|
CodeExtent::Misc(node_id) => node_id,
|
||||||
CodeExtent::Remainder(br) => br.block,
|
CodeExtent::Remainder(br) => br.block,
|
||||||
|
CodeExtent::DestructionScope(node_id) => node_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +168,8 @@ impl CodeExtent {
|
||||||
CodeExtent::Remainder(br) =>
|
CodeExtent::Remainder(br) =>
|
||||||
CodeExtent::Remainder(BlockRemainder {
|
CodeExtent::Remainder(BlockRemainder {
|
||||||
block: f_id(br.block), first_statement_index: br.first_statement_index }),
|
block: f_id(br.block), first_statement_index: br.first_statement_index }),
|
||||||
|
CodeExtent::DestructionScope(node_id) =>
|
||||||
|
CodeExtent::DestructionScope(f_id(node_id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +180,8 @@ impl CodeExtent {
|
||||||
match ast_map.find(self.node_id()) {
|
match ast_map.find(self.node_id()) {
|
||||||
Some(ast_map::NodeBlock(ref blk)) => {
|
Some(ast_map::NodeBlock(ref blk)) => {
|
||||||
match *self {
|
match *self {
|
||||||
CodeExtent::Misc(_) => Some(blk.span),
|
CodeExtent::Misc(_) |
|
||||||
|
CodeExtent::DestructionScope(_) => Some(blk.span),
|
||||||
|
|
||||||
CodeExtent::Remainder(r) => {
|
CodeExtent::Remainder(r) => {
|
||||||
assert_eq!(r.block, blk.id);
|
assert_eq!(r.block, blk.id);
|
||||||
|
@ -455,7 +531,7 @@ impl RegionMaps {
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::ReScope(sub_scope), ty::ReFree(ref fr)) => {
|
(ty::ReScope(sub_scope), ty::ReFree(ref fr)) => {
|
||||||
self.is_subscope_of(sub_scope, fr.scope)
|
self.is_subscope_of(sub_scope, fr.scope.to_code_extent())
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::ReFree(sub_fr), ty::ReFree(super_fr)) => {
|
(ty::ReFree(sub_fr), ty::ReFree(super_fr)) => {
|
||||||
|
@ -567,7 +643,18 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
|
||||||
let prev_cx = visitor.cx;
|
let prev_cx = visitor.cx;
|
||||||
|
|
||||||
let blk_scope = CodeExtent::Misc(blk.id);
|
let blk_scope = CodeExtent::Misc(blk.id);
|
||||||
|
// If block was previously marked as a terminating scope during
|
||||||
|
// the recursive visit of its parent node in the AST, then we need
|
||||||
|
// to account for the destruction scope representing the extent of
|
||||||
|
// the destructors that run immediately after the the block itself
|
||||||
|
// completes.
|
||||||
|
if visitor.region_maps.terminating_scopes.borrow().contains(&blk_scope) {
|
||||||
|
let dtor_scope = CodeExtent::DestructionScope(blk.id);
|
||||||
|
record_superlifetime(visitor, dtor_scope, blk.span);
|
||||||
|
visitor.region_maps.record_encl_scope(blk_scope, dtor_scope);
|
||||||
|
} else {
|
||||||
record_superlifetime(visitor, blk_scope, blk.span);
|
record_superlifetime(visitor, blk_scope, blk.span);
|
||||||
|
}
|
||||||
|
|
||||||
// We treat the tail expression in the block (if any) somewhat
|
// We treat the tail expression in the block (if any) somewhat
|
||||||
// differently from the statements. The issue has to do with
|
// differently from the statements. The issue has to do with
|
||||||
|
@ -675,7 +762,9 @@ fn resolve_stmt(visitor: &mut RegionResolutionVisitor, stmt: &ast::Stmt) {
|
||||||
// statement plus its destructors, and thus the extent for which
|
// statement plus its destructors, and thus the extent for which
|
||||||
// regions referenced by the destructors need to survive.
|
// regions referenced by the destructors need to survive.
|
||||||
visitor.region_maps.mark_as_terminating_scope(stmt_scope);
|
visitor.region_maps.mark_as_terminating_scope(stmt_scope);
|
||||||
record_superlifetime(visitor, stmt_scope, stmt.span);
|
let dtor_scope = CodeExtent::DestructionScope(stmt_id);
|
||||||
|
visitor.region_maps.record_encl_scope(stmt_scope, dtor_scope);
|
||||||
|
record_superlifetime(visitor, dtor_scope, stmt.span);
|
||||||
|
|
||||||
let prev_parent = visitor.cx.parent;
|
let prev_parent = visitor.cx.parent;
|
||||||
visitor.cx.parent = InnermostEnclosingExpr::Some(stmt_id);
|
visitor.cx.parent = InnermostEnclosingExpr::Some(stmt_id);
|
||||||
|
@ -687,15 +776,30 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
|
||||||
debug!("resolve_expr(expr.id={:?})", expr.id);
|
debug!("resolve_expr(expr.id={:?})", expr.id);
|
||||||
|
|
||||||
let expr_scope = CodeExtent::Misc(expr.id);
|
let expr_scope = CodeExtent::Misc(expr.id);
|
||||||
|
// If expr was previously marked as a terminating scope during the
|
||||||
|
// recursive visit of its parent node in the AST, then we need to
|
||||||
|
// account for the destruction scope representing the extent of
|
||||||
|
// the destructors that run immediately after the the expression
|
||||||
|
// itself completes.
|
||||||
|
if visitor.region_maps.terminating_scopes.borrow().contains(&expr_scope) {
|
||||||
|
let dtor_scope = CodeExtent::DestructionScope(expr.id);
|
||||||
|
record_superlifetime(visitor, dtor_scope, expr.span);
|
||||||
|
visitor.region_maps.record_encl_scope(expr_scope, dtor_scope);
|
||||||
|
} else {
|
||||||
record_superlifetime(visitor, expr_scope, expr.span);
|
record_superlifetime(visitor, expr_scope, expr.span);
|
||||||
|
}
|
||||||
|
|
||||||
let prev_cx = visitor.cx;
|
let prev_cx = visitor.cx;
|
||||||
visitor.cx.parent = InnermostEnclosingExpr::Some(expr.id);
|
visitor.cx.parent = InnermostEnclosingExpr::Some(expr.id);
|
||||||
|
|
||||||
{
|
{
|
||||||
let region_maps = &mut visitor.region_maps;
|
let region_maps = &mut visitor.region_maps;
|
||||||
let terminating = |id| {
|
let terminating = |e: &P<ast::Expr>| {
|
||||||
let scope = CodeExtent::from_node_id(id);
|
let scope = CodeExtent::from_node_id(e.id);
|
||||||
|
region_maps.mark_as_terminating_scope(scope)
|
||||||
|
};
|
||||||
|
let terminating_block = |b: &P<ast::Block>| {
|
||||||
|
let scope = CodeExtent::from_node_id(b.id);
|
||||||
region_maps.mark_as_terminating_scope(scope)
|
region_maps.mark_as_terminating_scope(scope)
|
||||||
};
|
};
|
||||||
match expr.node {
|
match expr.node {
|
||||||
|
@ -707,26 +811,26 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
|
||||||
ast::ExprBinary(codemap::Spanned { node: ast::BiOr, .. }, _, ref r) => {
|
ast::ExprBinary(codemap::Spanned { node: ast::BiOr, .. }, _, ref r) => {
|
||||||
// For shortcircuiting operators, mark the RHS as a terminating
|
// For shortcircuiting operators, mark the RHS as a terminating
|
||||||
// scope since it only executes conditionally.
|
// scope since it only executes conditionally.
|
||||||
terminating(r.id);
|
terminating(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprIf(_, ref then, Some(ref otherwise)) => {
|
ast::ExprIf(_, ref then, Some(ref otherwise)) => {
|
||||||
terminating(then.id);
|
terminating_block(then);
|
||||||
terminating(otherwise.id);
|
terminating(otherwise);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprIf(ref expr, ref then, None) => {
|
ast::ExprIf(ref expr, ref then, None) => {
|
||||||
terminating(expr.id);
|
terminating(expr);
|
||||||
terminating(then.id);
|
terminating_block(then);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprLoop(ref body, _) => {
|
ast::ExprLoop(ref body, _) => {
|
||||||
terminating(body.id);
|
terminating_block(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprWhile(ref expr, ref body, _) => {
|
ast::ExprWhile(ref expr, ref body, _) => {
|
||||||
terminating(expr.id);
|
terminating(expr);
|
||||||
terminating(body.id);
|
terminating_block(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprMatch(..) => {
|
ast::ExprMatch(..) => {
|
||||||
|
@ -1021,6 +1125,9 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
|
||||||
|
|
||||||
let body_scope = CodeExtent::from_node_id(body.id);
|
let body_scope = CodeExtent::from_node_id(body.id);
|
||||||
visitor.region_maps.mark_as_terminating_scope(body_scope);
|
visitor.region_maps.mark_as_terminating_scope(body_scope);
|
||||||
|
let dtor_scope = CodeExtent::DestructionScope(body.id);
|
||||||
|
visitor.region_maps.record_encl_scope(body_scope, dtor_scope);
|
||||||
|
record_superlifetime(visitor, dtor_scope, body.span);
|
||||||
|
|
||||||
let outer_cx = visitor.cx;
|
let outer_cx = visitor.cx;
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub enum DefRegion {
|
||||||
/* lifetime decl */ ast::NodeId),
|
/* lifetime decl */ ast::NodeId),
|
||||||
DefLateBoundRegion(ty::DebruijnIndex,
|
DefLateBoundRegion(ty::DebruijnIndex,
|
||||||
/* lifetime decl */ ast::NodeId),
|
/* lifetime decl */ ast::NodeId),
|
||||||
DefFreeRegion(/* block scope */ region::CodeExtent,
|
DefFreeRegion(/* block scope */ region::DestructionScopeData,
|
||||||
/* lifetime decl */ ast::NodeId),
|
/* lifetime decl */ ast::NodeId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ enum ScopeChain<'a> {
|
||||||
LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>),
|
LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>),
|
||||||
/// lifetimes introduced by items within a code block are scoped
|
/// lifetimes introduced by items within a code block are scoped
|
||||||
/// to that block.
|
/// to that block.
|
||||||
BlockScope(region::CodeExtent, Scope<'a>),
|
BlockScope(region::DestructionScopeData, Scope<'a>),
|
||||||
RootScope
|
RootScope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_block(&mut self, b: &ast::Block) {
|
fn visit_block(&mut self, b: &ast::Block) {
|
||||||
self.with(BlockScope(region::CodeExtent::from_node_id(b.id), self.scope),
|
self.with(BlockScope(region::DestructionScopeData::new(b.id),
|
||||||
|
self.scope),
|
||||||
|_, this| visit::walk_block(this, b));
|
|_, this| visit::walk_block(this, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,9 +394,13 @@ impl<'a> LifetimeContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_free_lifetime_ref(&mut self,
|
fn resolve_free_lifetime_ref(&mut self,
|
||||||
scope_data: region::CodeExtent,
|
scope_data: region::DestructionScopeData,
|
||||||
lifetime_ref: &ast::Lifetime,
|
lifetime_ref: &ast::Lifetime,
|
||||||
scope: Scope) {
|
scope: Scope) {
|
||||||
|
debug!("resolve_free_lifetime_ref \
|
||||||
|
scope_data: {:?} lifetime_ref: {:?} scope: {:?}",
|
||||||
|
scope_data, lifetime_ref, scope);
|
||||||
|
|
||||||
// Walk up the scope chain, tracking the outermost free scope,
|
// Walk up the scope chain, tracking the outermost free scope,
|
||||||
// until we encounter a scope that contains the named lifetime
|
// until we encounter a scope that contains the named lifetime
|
||||||
// or we run out of scopes.
|
// or we run out of scopes.
|
||||||
|
@ -403,6 +408,9 @@ impl<'a> LifetimeContext<'a> {
|
||||||
let mut scope = scope;
|
let mut scope = scope;
|
||||||
let mut search_result = None;
|
let mut search_result = None;
|
||||||
loop {
|
loop {
|
||||||
|
debug!("resolve_free_lifetime_ref \
|
||||||
|
scope_data: {:?} scope: {:?} search_result: {:?}",
|
||||||
|
scope_data, scope, search_result);
|
||||||
match *scope {
|
match *scope {
|
||||||
BlockScope(blk_scope_data, s) => {
|
BlockScope(blk_scope_data, s) => {
|
||||||
scope_data = blk_scope_data;
|
scope_data = blk_scope_data;
|
||||||
|
|
|
@ -72,6 +72,8 @@ use std::cell::{Cell, RefCell};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Writer, SipHasher, Hasher};
|
use std::hash::{Hash, Writer, SipHasher, Hasher};
|
||||||
|
#[cfg(stage0)]
|
||||||
|
use std::marker;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -931,6 +933,26 @@ pub struct TyS<'tcx> {
|
||||||
|
|
||||||
// the maximal depth of any bound regions appearing in this type.
|
// the maximal depth of any bound regions appearing in this type.
|
||||||
region_depth: u32,
|
region_depth: u32,
|
||||||
|
|
||||||
|
// force the lifetime to be invariant to work-around
|
||||||
|
// region-inference issues with a covariant lifetime.
|
||||||
|
#[cfg(stage0)]
|
||||||
|
marker: ShowInvariantLifetime<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
|
struct ShowInvariantLifetime<'a>(marker::InvariantLifetime<'a>);
|
||||||
|
#[cfg(stage0)]
|
||||||
|
impl<'a> ShowInvariantLifetime<'a> {
|
||||||
|
fn new() -> ShowInvariantLifetime<'a> {
|
||||||
|
ShowInvariantLifetime(marker::InvariantLifetime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(stage0)]
|
||||||
|
impl<'a> fmt::Debug for ShowInvariantLifetime<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "InvariantLifetime")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for TypeFlags {
|
impl fmt::Debug for TypeFlags {
|
||||||
|
@ -939,9 +961,18 @@ impl fmt::Debug for TypeFlags {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
|
impl<'tcx> PartialEq for TyS<'tcx> {
|
||||||
|
fn eq<'a,'b>(&'a self, other: &'b TyS<'tcx>) -> bool {
|
||||||
|
let other: &'a TyS<'tcx> = unsafe { mem::transmute(other) };
|
||||||
|
(self as *const _) == (other as *const _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(stage0))]
|
||||||
impl<'tcx> PartialEq for TyS<'tcx> {
|
impl<'tcx> PartialEq for TyS<'tcx> {
|
||||||
fn eq(&self, other: &TyS<'tcx>) -> bool {
|
fn eq(&self, other: &TyS<'tcx>) -> bool {
|
||||||
(self as *const _) == (other as *const _)
|
// (self as *const _) == (other as *const _)
|
||||||
|
(self as *const TyS<'tcx>) == (other as *const TyS<'tcx>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'tcx> Eq for TyS<'tcx> {}
|
impl<'tcx> Eq for TyS<'tcx> {}
|
||||||
|
@ -1174,7 +1205,9 @@ pub enum Region {
|
||||||
/// region parameters.
|
/// region parameters.
|
||||||
ReFree(FreeRegion),
|
ReFree(FreeRegion),
|
||||||
|
|
||||||
/// A concrete region naming some expression within the current function.
|
/// A concrete region naming some statically determined extent
|
||||||
|
/// (e.g. an expression or sequence of statements) within the
|
||||||
|
/// current function.
|
||||||
ReScope(region::CodeExtent),
|
ReScope(region::CodeExtent),
|
||||||
|
|
||||||
/// Static data that has an "infinite" lifetime. Top in the region lattice.
|
/// Static data that has an "infinite" lifetime. Top in the region lattice.
|
||||||
|
@ -1296,7 +1329,7 @@ impl Region {
|
||||||
/// A "free" region `fr` can be interpreted as "some region
|
/// A "free" region `fr` can be interpreted as "some region
|
||||||
/// at least as big as the scope `fr.scope`".
|
/// at least as big as the scope `fr.scope`".
|
||||||
pub struct FreeRegion {
|
pub struct FreeRegion {
|
||||||
pub scope: region::CodeExtent,
|
pub scope: region::DestructionScopeData,
|
||||||
pub bound_region: BoundRegion
|
pub bound_region: BoundRegion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2473,11 +2506,17 @@ fn intern_ty<'tcx>(type_arena: &'tcx TypedArena<TyS<'tcx>>,
|
||||||
|
|
||||||
let flags = FlagComputation::for_sty(&st);
|
let flags = FlagComputation::for_sty(&st);
|
||||||
|
|
||||||
let ty = type_arena.alloc(TyS {
|
let ty = match () {
|
||||||
sty: st,
|
#[cfg(stage0)]
|
||||||
|
() => type_arena.alloc(TyS { sty: st,
|
||||||
flags: flags.flags,
|
flags: flags.flags,
|
||||||
region_depth: flags.depth,
|
region_depth: flags.depth,
|
||||||
});
|
marker: ShowInvariantLifetime::new(), }),
|
||||||
|
#[cfg(not(stage0))]
|
||||||
|
() => type_arena.alloc(TyS { sty: st,
|
||||||
|
flags: flags.flags,
|
||||||
|
region_depth: flags.depth, }),
|
||||||
|
};
|
||||||
|
|
||||||
debug!("Interned type: {:?} Pointer: {:?}",
|
debug!("Interned type: {:?} Pointer: {:?}",
|
||||||
ty, ty as *const _);
|
ty, ty as *const _);
|
||||||
|
@ -4192,12 +4231,16 @@ pub fn ty_region(tcx: &ctxt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free_region_from_def(free_id: ast::NodeId, def: &RegionParameterDef)
|
pub fn free_region_from_def(outlives_extent: region::DestructionScopeData,
|
||||||
|
def: &RegionParameterDef)
|
||||||
-> ty::Region
|
-> ty::Region
|
||||||
{
|
{
|
||||||
ty::ReFree(ty::FreeRegion { scope: region::CodeExtent::from_node_id(free_id),
|
let ret =
|
||||||
|
ty::ReFree(ty::FreeRegion { scope: outlives_extent,
|
||||||
bound_region: ty::BrNamed(def.def_id,
|
bound_region: ty::BrNamed(def.def_id,
|
||||||
def.name) })
|
def.name) });
|
||||||
|
debug!("free_region_from_def returns {:?}", ret);
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the type of a pattern as a monotype. Like @expr_ty, this function
|
// Returns the type of a pattern as a monotype. Like @expr_ty, this function
|
||||||
|
@ -6252,9 +6295,11 @@ pub fn construct_free_substs<'a,'tcx>(
|
||||||
let mut types = VecPerParamSpace::empty();
|
let mut types = VecPerParamSpace::empty();
|
||||||
push_types_from_defs(tcx, &mut types, generics.types.as_slice());
|
push_types_from_defs(tcx, &mut types, generics.types.as_slice());
|
||||||
|
|
||||||
|
let free_id_outlive = region::DestructionScopeData::new(free_id);
|
||||||
|
|
||||||
// map bound 'a => free 'a
|
// map bound 'a => free 'a
|
||||||
let mut regions = VecPerParamSpace::empty();
|
let mut regions = VecPerParamSpace::empty();
|
||||||
push_region_params(&mut regions, free_id, generics.regions.as_slice());
|
push_region_params(&mut regions, free_id_outlive, generics.regions.as_slice());
|
||||||
|
|
||||||
return Substs {
|
return Substs {
|
||||||
types: types,
|
types: types,
|
||||||
|
@ -6262,11 +6307,11 @@ pub fn construct_free_substs<'a,'tcx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,
|
fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,
|
||||||
free_id: ast::NodeId,
|
all_outlive_extent: region::DestructionScopeData,
|
||||||
region_params: &[RegionParameterDef])
|
region_params: &[RegionParameterDef])
|
||||||
{
|
{
|
||||||
for r in region_params {
|
for r in region_params {
|
||||||
regions.push(r.space, ty::free_region_from_def(free_id, r));
|
regions.push(r.space, ty::free_region_from_def(all_outlive_extent, r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6295,14 +6340,14 @@ pub fn construct_parameter_environment<'a,'tcx>(
|
||||||
//
|
//
|
||||||
|
|
||||||
let free_substs = construct_free_substs(tcx, generics, free_id);
|
let free_substs = construct_free_substs(tcx, generics, free_id);
|
||||||
let free_id_scope = region::CodeExtent::from_node_id(free_id);
|
let free_id_outlive = region::DestructionScopeData::new(free_id);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Compute the bounds on Self and the type parameters.
|
// Compute the bounds on Self and the type parameters.
|
||||||
//
|
//
|
||||||
|
|
||||||
let bounds = generics.to_bounds(tcx, &free_substs);
|
let bounds = generics.to_bounds(tcx, &free_substs);
|
||||||
let bounds = liberate_late_bound_regions(tcx, free_id_scope, &ty::Binder(bounds));
|
let bounds = liberate_late_bound_regions(tcx, free_id_outlive, &ty::Binder(bounds));
|
||||||
let predicates = bounds.predicates.into_vec();
|
let predicates = bounds.predicates.into_vec();
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -6335,7 +6380,7 @@ pub fn construct_parameter_environment<'a,'tcx>(
|
||||||
let unnormalized_env = ty::ParameterEnvironment {
|
let unnormalized_env = ty::ParameterEnvironment {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
free_substs: free_substs,
|
free_substs: free_substs,
|
||||||
implicit_region_bound: ty::ReScope(free_id_scope),
|
implicit_region_bound: ty::ReScope(free_id_outlive.to_code_extent()),
|
||||||
caller_bounds: predicates,
|
caller_bounds: predicates,
|
||||||
selection_cache: traits::SelectionCache::new(),
|
selection_cache: traits::SelectionCache::new(),
|
||||||
};
|
};
|
||||||
|
@ -6603,14 +6648,14 @@ impl<'tcx> AutoDerefRef<'tcx> {
|
||||||
/// `scope_id`.
|
/// `scope_id`.
|
||||||
pub fn liberate_late_bound_regions<'tcx, T>(
|
pub fn liberate_late_bound_regions<'tcx, T>(
|
||||||
tcx: &ty::ctxt<'tcx>,
|
tcx: &ty::ctxt<'tcx>,
|
||||||
scope: region::CodeExtent,
|
all_outlive_scope: region::DestructionScopeData,
|
||||||
value: &Binder<T>)
|
value: &Binder<T>)
|
||||||
-> T
|
-> T
|
||||||
where T : TypeFoldable<'tcx> + Repr<'tcx>
|
where T : TypeFoldable<'tcx> + Repr<'tcx>
|
||||||
{
|
{
|
||||||
replace_late_bound_regions(
|
replace_late_bound_regions(
|
||||||
tcx, value,
|
tcx, value,
|
||||||
|br| ty::ReFree(ty::FreeRegion{scope: scope, bound_region: br})).0
|
|br| ty::ReFree(ty::FreeRegion{scope: all_outlive_scope, bound_region: br})).0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_late_bound_regions<'tcx, T>(
|
pub fn count_late_bound_regions<'tcx, T>(
|
||||||
|
|
|
@ -113,6 +113,10 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
|
||||||
};
|
};
|
||||||
let scope_decorated_tag = match scope {
|
let scope_decorated_tag = match scope {
|
||||||
region::CodeExtent::Misc(_) => tag,
|
region::CodeExtent::Misc(_) => tag,
|
||||||
|
region::CodeExtent::DestructionScope(_) => {
|
||||||
|
new_string = format!("destruction scope surrounding {}", tag);
|
||||||
|
new_string.as_slice()
|
||||||
|
}
|
||||||
region::CodeExtent::Remainder(r) => {
|
region::CodeExtent::Remainder(r) => {
|
||||||
new_string = format!("block suffix following statement {}",
|
new_string = format!("block suffix following statement {}",
|
||||||
r.first_statement_index);
|
r.first_statement_index);
|
||||||
|
@ -135,7 +139,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match cx.map.find(fr.scope.node_id()) {
|
match cx.map.find(fr.scope.node_id) {
|
||||||
Some(ast_map::NodeBlock(ref blk)) => {
|
Some(ast_map::NodeBlock(ref blk)) => {
|
||||||
let (msg, opt_span) = explain_span(cx, "block", blk.span);
|
let (msg, opt_span) = explain_span(cx, "block", blk.span);
|
||||||
(format!("{} {}", prefix, msg), opt_span)
|
(format!("{} {}", prefix, msg), opt_span)
|
||||||
|
@ -921,7 +925,7 @@ impl<'tcx> UserString<'tcx> for ty::Region {
|
||||||
impl<'tcx> Repr<'tcx> for ty::FreeRegion {
|
impl<'tcx> Repr<'tcx> for ty::FreeRegion {
|
||||||
fn repr(&self, tcx: &ctxt) -> String {
|
fn repr(&self, tcx: &ctxt) -> String {
|
||||||
format!("ReFree({}, {})",
|
format!("ReFree({}, {})",
|
||||||
self.scope.node_id(),
|
self.scope.repr(tcx),
|
||||||
self.bound_region.repr(tcx))
|
self.bound_region.repr(tcx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -931,12 +935,23 @@ impl<'tcx> Repr<'tcx> for region::CodeExtent {
|
||||||
match *self {
|
match *self {
|
||||||
region::CodeExtent::Misc(node_id) =>
|
region::CodeExtent::Misc(node_id) =>
|
||||||
format!("Misc({})", node_id),
|
format!("Misc({})", node_id),
|
||||||
|
region::CodeExtent::DestructionScope(node_id) =>
|
||||||
|
format!("DestructionScope({})", node_id),
|
||||||
region::CodeExtent::Remainder(rem) =>
|
region::CodeExtent::Remainder(rem) =>
|
||||||
format!("Remainder({}, {})", rem.block, rem.first_statement_index),
|
format!("Remainder({}, {})", rem.block, rem.first_statement_index),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Repr<'tcx> for region::DestructionScopeData {
|
||||||
|
fn repr(&self, _tcx: &ctxt) -> String {
|
||||||
|
match *self {
|
||||||
|
region::DestructionScopeData{ node_id } =>
|
||||||
|
format!("DestructionScopeData {{ node_id: {} }}", node_id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> Repr<'tcx> for ast::DefId {
|
impl<'tcx> Repr<'tcx> for ast::DefId {
|
||||||
fn repr(&self, tcx: &ctxt) -> String {
|
fn repr(&self, tcx: &ctxt) -> String {
|
||||||
// Unfortunately, there seems to be no way to attempt to print
|
// Unfortunately, there seems to be no way to attempt to print
|
||||||
|
|
|
@ -286,7 +286,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
|
||||||
let loan_scope = match loan_region {
|
let loan_scope = match loan_region {
|
||||||
ty::ReScope(scope) => scope,
|
ty::ReScope(scope) => scope,
|
||||||
|
|
||||||
ty::ReFree(ref fr) => fr.scope,
|
ty::ReFree(ref fr) => fr.scope.to_code_extent(),
|
||||||
|
|
||||||
ty::ReStatic => {
|
ty::ReStatic => {
|
||||||
// If we get here, an error must have been
|
// If we get here, an error must have been
|
||||||
|
|
|
@ -15,7 +15,7 @@ use diagnostic::Emitter;
|
||||||
use driver;
|
use driver;
|
||||||
use rustc_resolve as resolve;
|
use rustc_resolve as resolve;
|
||||||
use rustc_typeck::middle::lang_items;
|
use rustc_typeck::middle::lang_items;
|
||||||
use rustc_typeck::middle::region::{self, CodeExtent};
|
use rustc_typeck::middle::region::{self, CodeExtent, DestructionScopeData};
|
||||||
use rustc_typeck::middle::resolve_lifetime;
|
use rustc_typeck::middle::resolve_lifetime;
|
||||||
use rustc_typeck::middle::stability;
|
use rustc_typeck::middle::stability;
|
||||||
use rustc_typeck::middle::subst;
|
use rustc_typeck::middle::subst;
|
||||||
|
@ -325,7 +325,7 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn re_free(&self, nid: ast::NodeId, id: u32) -> ty::Region {
|
pub fn re_free(&self, nid: ast::NodeId, id: u32) -> ty::Region {
|
||||||
ty::ReFree(ty::FreeRegion { scope: CodeExtent::from_node_id(nid),
|
ty::ReFree(ty::FreeRegion { scope: DestructionScopeData::new(nid),
|
||||||
bound_region: ty::BrAnon(id)})
|
bound_region: ty::BrAnon(id)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2196,7 +2196,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||||
|
|
||||||
// Search for external modules.
|
// Search for external modules.
|
||||||
if namespace == TypeNS {
|
if namespace == TypeNS {
|
||||||
if let Some(module) = module_.external_module_children.borrow().get(&name).cloned() {
|
// FIXME (21114): In principle unclear `child` *has* to be lifted.
|
||||||
|
let child = module_.external_module_children.borrow().get(&name).cloned();
|
||||||
|
if let Some(module) = child {
|
||||||
let name_bindings =
|
let name_bindings =
|
||||||
Rc::new(Resolver::create_name_bindings_from_module(module));
|
Rc::new(Resolver::create_name_bindings_from_module(module));
|
||||||
debug!("lower name bindings succeeded");
|
debug!("lower name bindings succeeded");
|
||||||
|
@ -2481,7 +2483,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||||
|
|
||||||
// Finally, search through external children.
|
// Finally, search through external children.
|
||||||
if namespace == TypeNS {
|
if namespace == TypeNS {
|
||||||
if let Some(module) = module_.external_module_children.borrow().get(&name).cloned() {
|
// FIXME (21114): In principle unclear `child` *has* to be lifted.
|
||||||
|
let child = module_.external_module_children.borrow().get(&name).cloned();
|
||||||
|
if let Some(module) = child {
|
||||||
let name_bindings =
|
let name_bindings =
|
||||||
Rc::new(Resolver::create_name_bindings_from_module(module));
|
Rc::new(Resolver::create_name_bindings_from_module(module));
|
||||||
return Success((Target::new(module_,
|
return Success((Target::new(module_,
|
||||||
|
|
|
@ -130,12 +130,17 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
|
||||||
// this new AST scope had better be its immediate child.
|
// this new AST scope had better be its immediate child.
|
||||||
let top_scope = self.top_ast_scope();
|
let top_scope = self.top_ast_scope();
|
||||||
if top_scope.is_some() {
|
if top_scope.is_some() {
|
||||||
assert_eq!(self.ccx
|
assert!((self.ccx
|
||||||
.tcx()
|
.tcx()
|
||||||
.region_maps
|
.region_maps
|
||||||
.opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id))
|
.opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id))
|
||||||
.map(|s|s.node_id()),
|
.map(|s|s.node_id()) == top_scope)
|
||||||
top_scope);
|
||
|
||||||
|
(self.ccx
|
||||||
|
.tcx()
|
||||||
|
.region_maps
|
||||||
|
.opt_encl_scope(region::CodeExtent::DestructionScope(debug_loc.id))
|
||||||
|
.map(|s|s.node_id()) == top_scope));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push_scope(CleanupScope::new(AstScopeKind(debug_loc.id),
|
self.push_scope(CleanupScope::new(AstScopeKind(debug_loc.id),
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
use super::{check_fn, Expectation, FnCtxt};
|
use super::{check_fn, Expectation, FnCtxt};
|
||||||
|
|
||||||
use astconv;
|
use astconv;
|
||||||
use middle::region::CodeExtent;
|
use middle::region;
|
||||||
use middle::subst;
|
use middle::subst;
|
||||||
use middle::ty::{self, ToPolyTraitRef, Ty};
|
use middle::ty::{self, ToPolyTraitRef, Ty};
|
||||||
use rscope::RegionScope;
|
use rscope::RegionScope;
|
||||||
|
@ -78,7 +78,9 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||||
fcx.write_ty(expr.id, closure_type);
|
fcx.write_ty(expr.id, closure_type);
|
||||||
|
|
||||||
let fn_sig =
|
let fn_sig =
|
||||||
ty::liberate_late_bound_regions(fcx.tcx(), CodeExtent::from_node_id(body.id), &fn_ty.sig);
|
ty::liberate_late_bound_regions(fcx.tcx(),
|
||||||
|
region::DestructionScopeData::new(body.id),
|
||||||
|
&fn_ty.sig);
|
||||||
|
|
||||||
check_fn(fcx.ccx,
|
check_fn(fcx.ccx,
|
||||||
ast::Unsafety::Normal,
|
ast::Unsafety::Normal,
|
||||||
|
|
|
@ -0,0 +1,292 @@
|
||||||
|
// Copyright 2014-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.
|
||||||
|
|
||||||
|
use check::regionck::{self, Rcx};
|
||||||
|
|
||||||
|
use middle::infer;
|
||||||
|
use middle::region;
|
||||||
|
use middle::subst;
|
||||||
|
use middle::ty::{self, Ty};
|
||||||
|
use util::ppaux::{Repr};
|
||||||
|
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
|
||||||
|
pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
||||||
|
typ: ty::Ty<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
scope: region::CodeExtent) {
|
||||||
|
debug!("check_safety_of_destructor_if_necessary typ: {} scope: {:?}",
|
||||||
|
typ.repr(rcx.tcx()), scope);
|
||||||
|
|
||||||
|
// types that have been traversed so far by `traverse_type_if_unseen`
|
||||||
|
let mut breadcrumbs: Vec<Ty<'tcx>> = Vec::new();
|
||||||
|
|
||||||
|
iterate_over_potentially_unsafe_regions_in_type(
|
||||||
|
rcx,
|
||||||
|
&mut breadcrumbs,
|
||||||
|
typ,
|
||||||
|
span,
|
||||||
|
scope,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
|
||||||
|
rcx: &mut Rcx<'a, 'tcx>,
|
||||||
|
breadcrumbs: &mut Vec<Ty<'tcx>>,
|
||||||
|
ty_root: ty::Ty<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
scope: region::CodeExtent,
|
||||||
|
depth: uint)
|
||||||
|
{
|
||||||
|
let origin = |&:| infer::SubregionOrigin::SafeDestructor(span);
|
||||||
|
let mut walker = ty_root.walk();
|
||||||
|
let opt_phantom_data_def_id = rcx.tcx().lang_items.phantom_data();
|
||||||
|
|
||||||
|
let destructor_for_type = rcx.tcx().destructor_for_type.borrow();
|
||||||
|
|
||||||
|
while let Some(typ) = walker.next() {
|
||||||
|
// Avoid recursing forever.
|
||||||
|
if breadcrumbs.contains(&typ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
breadcrumbs.push(typ);
|
||||||
|
|
||||||
|
// If we encounter `PhantomData<T>`, then we should replace it
|
||||||
|
// with `T`, the type it represents as owned by the
|
||||||
|
// surrounding context, before doing further analysis.
|
||||||
|
let typ = if let ty::ty_struct(struct_did, substs) = typ.sty {
|
||||||
|
if opt_phantom_data_def_id == Some(struct_did) {
|
||||||
|
let item_type = ty::lookup_item_type(rcx.tcx(), struct_did);
|
||||||
|
let tp_def = item_type.generics.types
|
||||||
|
.opt_get(subst::TypeSpace, 0).unwrap();
|
||||||
|
let new_typ = substs.type_for_def(tp_def);
|
||||||
|
debug!("replacing phantom {} with {}",
|
||||||
|
typ.repr(rcx.tcx()), new_typ.repr(rcx.tcx()));
|
||||||
|
new_typ
|
||||||
|
} else {
|
||||||
|
typ
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
typ
|
||||||
|
};
|
||||||
|
|
||||||
|
let opt_type_did = match typ.sty {
|
||||||
|
ty::ty_struct(struct_did, _) => Some(struct_did),
|
||||||
|
ty::ty_enum(enum_did, _) => Some(enum_did),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let opt_dtor =
|
||||||
|
opt_type_did.and_then(|did| destructor_for_type.get(&did));
|
||||||
|
|
||||||
|
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
||||||
|
{}typ: {} scope: {:?} opt_dtor: {:?}",
|
||||||
|
(0..depth).map(|_| ' ').collect::<String>(),
|
||||||
|
typ.repr(rcx.tcx()), scope, opt_dtor);
|
||||||
|
|
||||||
|
// If `typ` has a destructor, then we must ensure that all
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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`.
|
||||||
|
//
|
||||||
|
// Of course, for the type expression passed in for any such
|
||||||
|
// 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).
|
||||||
|
|
||||||
|
let has_dtor_of_interest;
|
||||||
|
|
||||||
|
if let Some(&dtor_method_did) = opt_dtor {
|
||||||
|
let impl_did = ty::impl_of_method(rcx.tcx(), dtor_method_did)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
rcx.tcx().sess.span_bug(
|
||||||
|
span, "no Drop impl found for drop method")
|
||||||
|
});
|
||||||
|
|
||||||
|
let dtor_typescheme = ty::lookup_item_type(rcx.tcx(), impl_did);
|
||||||
|
let dtor_generics = dtor_typescheme.generics;
|
||||||
|
|
||||||
|
let has_pred_of_interest = dtor_generics.predicates.iter().any(|pred| {
|
||||||
|
// In `impl<T> Drop where ...`, we automatically
|
||||||
|
// assume some predicate will be meaningful and thus
|
||||||
|
// represents a type through which we could reach
|
||||||
|
// borrowed data. However, there can be implicit
|
||||||
|
// predicates (namely for Sized), and so we still need
|
||||||
|
// to walk through and filter out those cases.
|
||||||
|
|
||||||
|
let result = match *pred {
|
||||||
|
ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
|
||||||
|
let def_id = t_pred.trait_ref.def_id;
|
||||||
|
match rcx.tcx().lang_items.to_builtin_kind(def_id) {
|
||||||
|
Some(ty::BoundSend) |
|
||||||
|
Some(ty::BoundSized) |
|
||||||
|
Some(ty::BoundCopy) |
|
||||||
|
Some(ty::BoundSync) => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::Predicate::Equate(..) |
|
||||||
|
ty::Predicate::RegionOutlives(..) |
|
||||||
|
ty::Predicate::TypeOutlives(..) |
|
||||||
|
ty::Predicate::Projection(..) => {
|
||||||
|
// we assume all of these where-clauses may
|
||||||
|
// give the drop implementation the capabilty
|
||||||
|
// to access borrowed data.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if result {
|
||||||
|
debug!("typ: {} has interesting dtor due to generic preds, e.g. {}",
|
||||||
|
typ.repr(rcx.tcx()), pred.repr(rcx.tcx()));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
});
|
||||||
|
|
||||||
|
// In `impl<'a> Drop ...`, we automatically assume
|
||||||
|
// `'a` is meaningful and thus represents a bound
|
||||||
|
// through which we could reach borrowed data.
|
||||||
|
//
|
||||||
|
// 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`).
|
||||||
|
let has_region_param_of_interest =
|
||||||
|
dtor_generics.has_region_params(subst::TypeSpace);
|
||||||
|
|
||||||
|
has_dtor_of_interest =
|
||||||
|
has_region_param_of_interest ||
|
||||||
|
has_pred_of_interest;
|
||||||
|
|
||||||
|
if has_dtor_of_interest {
|
||||||
|
debug!("typ: {} has interesting dtor, due to \
|
||||||
|
region params: {} or pred: {}",
|
||||||
|
typ.repr(rcx.tcx()),
|
||||||
|
has_region_param_of_interest,
|
||||||
|
has_pred_of_interest);
|
||||||
|
} else {
|
||||||
|
debug!("typ: {} has dtor, but it is uninteresting",
|
||||||
|
typ.repr(rcx.tcx()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
debug!("typ: {} has no dtor, and thus is uninteresting",
|
||||||
|
typ.repr(rcx.tcx()));
|
||||||
|
has_dtor_of_interest = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_dtor_of_interest {
|
||||||
|
// If `typ` has a destructor, then we must ensure that all
|
||||||
|
// borrowed data reachable via `typ` must outlive the
|
||||||
|
// parent of `scope`. (It does not suffice for it to
|
||||||
|
// outlive `scope` because that could imply that the
|
||||||
|
// borrowed data is torn down in between the end of
|
||||||
|
// `scope` and when the destructor itself actually runs.)
|
||||||
|
|
||||||
|
let parent_region =
|
||||||
|
match rcx.tcx().region_maps.opt_encl_scope(scope) {
|
||||||
|
Some(parent_scope) => ty::ReScope(parent_scope),
|
||||||
|
None => rcx.tcx().sess.span_bug(
|
||||||
|
span, format!("no enclosing scope found for scope: {:?}",
|
||||||
|
scope).as_slice()),
|
||||||
|
};
|
||||||
|
|
||||||
|
regionck::type_must_outlive(rcx, origin(), typ, parent_region);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Okay, `typ` itself is itself not reachable by a
|
||||||
|
// destructor; but it may contain substructure that has a
|
||||||
|
// destructor.
|
||||||
|
|
||||||
|
match typ.sty {
|
||||||
|
ty::ty_struct(struct_did, substs) => {
|
||||||
|
// Don't recurse; we extract type's substructure,
|
||||||
|
// so do not process subparts of type expression.
|
||||||
|
walker.skip_current_subtree();
|
||||||
|
|
||||||
|
let fields =
|
||||||
|
ty::lookup_struct_fields(rcx.tcx(), struct_did);
|
||||||
|
for field in fields.iter() {
|
||||||
|
let field_type =
|
||||||
|
ty::lookup_field_type(rcx.tcx(),
|
||||||
|
struct_did,
|
||||||
|
field.id,
|
||||||
|
substs);
|
||||||
|
iterate_over_potentially_unsafe_regions_in_type(
|
||||||
|
rcx,
|
||||||
|
breadcrumbs,
|
||||||
|
field_type,
|
||||||
|
span,
|
||||||
|
scope,
|
||||||
|
depth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::ty_enum(enum_did, substs) => {
|
||||||
|
// Don't recurse; we extract type's substructure,
|
||||||
|
// so do not process subparts of type expression.
|
||||||
|
walker.skip_current_subtree();
|
||||||
|
|
||||||
|
let all_variant_info =
|
||||||
|
ty::substd_enum_variants(rcx.tcx(),
|
||||||
|
enum_did,
|
||||||
|
substs);
|
||||||
|
for variant_info in all_variant_info.iter() {
|
||||||
|
for argument_type in variant_info.args.iter() {
|
||||||
|
iterate_over_potentially_unsafe_regions_in_type(
|
||||||
|
rcx,
|
||||||
|
breadcrumbs,
|
||||||
|
*argument_type,
|
||||||
|
span,
|
||||||
|
scope,
|
||||||
|
depth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::ty_rptr(..) | ty::ty_ptr(_) | ty::ty_bare_fn(..) => {
|
||||||
|
// Don't recurse, since references, pointers,
|
||||||
|
// boxes, and bare functions don't own instances
|
||||||
|
// of the types appearing within them.
|
||||||
|
walker.skip_current_subtree();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// You might be tempted to pop breadcrumbs here after
|
||||||
|
// processing type's internals above, but then you hit
|
||||||
|
// exponential time blowup e.g. on
|
||||||
|
// compile-fail/huge-struct.rs. Instead, we do not remove
|
||||||
|
// anything from the breadcrumbs vector during any particular
|
||||||
|
// traversal, and instead clear it after the whole traversal
|
||||||
|
// is done.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -525,7 +525,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
|
||||||
trait_def_id);
|
trait_def_id);
|
||||||
|
|
||||||
let trait_impls = self.tcx().trait_impls.borrow();
|
let trait_impls = self.tcx().trait_impls.borrow();
|
||||||
let impl_def_ids = match trait_impls.get(&trait_def_id) {
|
let impl_def_ids = trait_impls.get(&trait_def_id);
|
||||||
|
let impl_def_ids = match impl_def_ids {
|
||||||
None => { return; }
|
None => { return; }
|
||||||
Some(impls) => impls,
|
Some(impls) => impls,
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,7 +90,7 @@ use middle::infer;
|
||||||
use middle::mem_categorization as mc;
|
use middle::mem_categorization as mc;
|
||||||
use middle::mem_categorization::McResult;
|
use middle::mem_categorization::McResult;
|
||||||
use middle::pat_util::{self, pat_id_map};
|
use middle::pat_util::{self, pat_id_map};
|
||||||
use middle::region::CodeExtent;
|
use middle::region::{self, CodeExtent};
|
||||||
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
|
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
|
||||||
use middle::traits;
|
use middle::traits;
|
||||||
use middle::ty::{FnSig, VariantInfo, TypeScheme};
|
use middle::ty::{FnSig, VariantInfo, TypeScheme};
|
||||||
|
@ -127,6 +127,7 @@ use syntax::ptr::P;
|
||||||
use syntax::visit::{self, Visitor};
|
use syntax::visit::{self, Visitor};
|
||||||
|
|
||||||
mod assoc;
|
mod assoc;
|
||||||
|
pub mod dropck;
|
||||||
pub mod _match;
|
pub mod _match;
|
||||||
pub mod vtable;
|
pub mod vtable;
|
||||||
pub mod writeback;
|
pub mod writeback;
|
||||||
|
@ -495,7 +496,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||||
let fn_sig =
|
let fn_sig =
|
||||||
fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs);
|
fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs);
|
||||||
let fn_sig =
|
let fn_sig =
|
||||||
liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig);
|
liberate_late_bound_regions(ccx.tcx,
|
||||||
|
region::DestructionScopeData::new(body.id),
|
||||||
|
&fn_sig);
|
||||||
let fn_sig =
|
let fn_sig =
|
||||||
inh.normalize_associated_types_in(&inh.param_env, body.span, body.id, &fn_sig);
|
inh.normalize_associated_types_in(&inh.param_env, body.span, body.id, &fn_sig);
|
||||||
|
|
||||||
|
@ -1686,7 +1689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
let mut bounds_checker = wf::BoundsChecker::new(self,
|
let mut bounds_checker = wf::BoundsChecker::new(self,
|
||||||
ast_t.span,
|
ast_t.span,
|
||||||
CodeExtent::from_node_id(self.body_id),
|
self.body_id,
|
||||||
None);
|
None);
|
||||||
bounds_checker.check_ty(t);
|
bounds_checker.check_ty(t);
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@
|
||||||
//! contents.
|
//! contents.
|
||||||
|
|
||||||
use astconv::AstConv;
|
use astconv::AstConv;
|
||||||
|
use check::dropck;
|
||||||
use check::FnCtxt;
|
use check::FnCtxt;
|
||||||
use check::regionmanip;
|
use check::regionmanip;
|
||||||
use check::vtable;
|
use check::vtable;
|
||||||
|
@ -171,6 +172,7 @@ pub struct Rcx<'a, 'tcx: 'a> {
|
||||||
|
|
||||||
// id of AST node being analyzed (the subject of the analysis).
|
// id of AST node being analyzed (the subject of the analysis).
|
||||||
subject: SubjectNode,
|
subject: SubjectNode,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the validity region of `def` -- that is, how long is `def` valid?
|
/// Returns the validity region of `def` -- that is, how long is `def` valid?
|
||||||
|
@ -198,7 +200,8 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
|
||||||
Rcx { fcx: fcx,
|
Rcx { fcx: fcx,
|
||||||
repeating_scope: initial_repeating_scope,
|
repeating_scope: initial_repeating_scope,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
region_bound_pairs: Vec::new() }
|
region_bound_pairs: Vec::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tcx(&self) -> &'a ty::ctxt<'tcx> {
|
pub fn tcx(&self) -> &'a ty::ctxt<'tcx> {
|
||||||
|
@ -469,6 +472,10 @@ fn constrain_bindings_in_pat(pat: &ast::Pat, rcx: &mut Rcx) {
|
||||||
type_of_node_must_outlive(
|
type_of_node_must_outlive(
|
||||||
rcx, infer::BindingTypeIsNotValidAtDecl(span),
|
rcx, infer::BindingTypeIsNotValidAtDecl(span),
|
||||||
id, var_region);
|
id, var_region);
|
||||||
|
|
||||||
|
let var_scope = tcx.region_maps.var_scope(id);
|
||||||
|
let typ = rcx.resolve_node_type(id);
|
||||||
|
dropck::check_safety_of_destructor_if_necessary(rcx, typ, span, var_scope);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,6 +524,40 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
|
||||||
*/
|
*/
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If necessary, constrain destructors in the unadjusted form of this
|
||||||
|
// expression.
|
||||||
|
let cmt_result = {
|
||||||
|
let mc = mc::MemCategorizationContext::new(rcx.fcx);
|
||||||
|
mc.cat_expr_unadjusted(expr)
|
||||||
|
};
|
||||||
|
match cmt_result {
|
||||||
|
Ok(head_cmt) => {
|
||||||
|
check_safety_of_rvalue_destructor_if_necessary(rcx,
|
||||||
|
head_cmt,
|
||||||
|
expr.span);
|
||||||
|
}
|
||||||
|
Err(..) => {
|
||||||
|
rcx.fcx.tcx().sess.span_note(expr.span,
|
||||||
|
"cat_expr_unadjusted Errd during dtor check");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If necessary, constrain destructors in this expression. This will be
|
||||||
|
// the adjusted form if there is an adjustment.
|
||||||
|
let cmt_result = {
|
||||||
|
let mc = mc::MemCategorizationContext::new(rcx.fcx);
|
||||||
|
mc.cat_expr(expr)
|
||||||
|
};
|
||||||
|
match cmt_result {
|
||||||
|
Ok(head_cmt) => {
|
||||||
|
check_safety_of_rvalue_destructor_if_necessary(rcx, head_cmt, expr.span);
|
||||||
|
}
|
||||||
|
Err(..) => {
|
||||||
|
rcx.fcx.tcx().sess.span_note(expr.span,
|
||||||
|
"cat_expr Errd during dtor check");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match expr.node {
|
match expr.node {
|
||||||
|
@ -995,6 +1036,33 @@ pub fn mk_subregion_due_to_dereference(rcx: &mut Rcx,
|
||||||
minimum_lifetime, maximum_lifetime)
|
minimum_lifetime, maximum_lifetime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_safety_of_rvalue_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
||||||
|
cmt: mc::cmt<'tcx>,
|
||||||
|
span: Span) {
|
||||||
|
match cmt.cat {
|
||||||
|
mc::cat_rvalue(region) => {
|
||||||
|
match region {
|
||||||
|
ty::ReScope(rvalue_scope) => {
|
||||||
|
let typ = rcx.resolve_type(cmt.ty);
|
||||||
|
dropck::check_safety_of_destructor_if_necessary(rcx,
|
||||||
|
typ,
|
||||||
|
span,
|
||||||
|
rvalue_scope);
|
||||||
|
}
|
||||||
|
ty::ReStatic => {}
|
||||||
|
region => {
|
||||||
|
rcx.tcx()
|
||||||
|
.sess
|
||||||
|
.span_bug(span,
|
||||||
|
format!("unexpected rvalue region in rvalue \
|
||||||
|
destructor safety checking: `{}`",
|
||||||
|
region.repr(rcx.tcx())).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Invoked on any index expression that occurs. Checks that if this is a slice being indexed, the
|
/// Invoked on any index expression that occurs. Checks that if this is a slice being indexed, the
|
||||||
/// lifetime of the pointer includes the deref expr.
|
/// lifetime of the pointer includes the deref expr.
|
||||||
|
@ -1404,7 +1472,7 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that all borrowed data reachable via `ty` outlives `region`.
|
/// Ensures that all borrowed data reachable via `ty` outlives `region`.
|
||||||
fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
|
||||||
origin: infer::SubregionOrigin<'tcx>,
|
origin: infer::SubregionOrigin<'tcx>,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
region: ty::Region)
|
region: ty::Region)
|
||||||
|
|
|
@ -145,8 +145,10 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||||
let variants = lookup_fields(fcx);
|
let variants = lookup_fields(fcx);
|
||||||
let mut bounds_checker = BoundsChecker::new(fcx,
|
let mut bounds_checker = BoundsChecker::new(fcx,
|
||||||
item.span,
|
item.span,
|
||||||
region::CodeExtent::from_node_id(item.id),
|
item.id,
|
||||||
Some(&mut this.cache));
|
Some(&mut this.cache));
|
||||||
|
debug!("check_type_defn at bounds_checker.scope: {:?}", bounds_checker.scope);
|
||||||
|
|
||||||
for variant in &variants {
|
for variant in &variants {
|
||||||
for field in &variant.fields {
|
for field in &variant.fields {
|
||||||
// Regions are checked below.
|
// Regions are checked below.
|
||||||
|
@ -180,8 +182,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||||
self.with_fcx(item, |this, fcx| {
|
self.with_fcx(item, |this, fcx| {
|
||||||
let mut bounds_checker = BoundsChecker::new(fcx,
|
let mut bounds_checker = BoundsChecker::new(fcx,
|
||||||
item.span,
|
item.span,
|
||||||
region::CodeExtent::from_node_id(item.id),
|
item.id,
|
||||||
Some(&mut this.cache));
|
Some(&mut this.cache));
|
||||||
|
debug!("check_item_type at bounds_checker.scope: {:?}", bounds_checker.scope);
|
||||||
|
|
||||||
let type_scheme = ty::lookup_item_type(fcx.tcx(), local_def(item.id));
|
let type_scheme = ty::lookup_item_type(fcx.tcx(), local_def(item.id));
|
||||||
let item_ty = fcx.instantiate_type_scheme(item.span,
|
let item_ty = fcx.instantiate_type_scheme(item.span,
|
||||||
|
@ -196,12 +199,11 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||||
item: &ast::Item)
|
item: &ast::Item)
|
||||||
{
|
{
|
||||||
self.with_fcx(item, |this, fcx| {
|
self.with_fcx(item, |this, fcx| {
|
||||||
let item_scope = region::CodeExtent::from_node_id(item.id);
|
|
||||||
|
|
||||||
let mut bounds_checker = BoundsChecker::new(fcx,
|
let mut bounds_checker = BoundsChecker::new(fcx,
|
||||||
item.span,
|
item.span,
|
||||||
item_scope,
|
item.id,
|
||||||
Some(&mut this.cache));
|
Some(&mut this.cache));
|
||||||
|
debug!("check_impl at bounds_checker.scope: {:?}", bounds_checker.scope);
|
||||||
|
|
||||||
// Find the impl self type as seen from the "inside" --
|
// Find the impl self type as seen from the "inside" --
|
||||||
// that is, with all type parameters converted from bound
|
// that is, with all type parameters converted from bound
|
||||||
|
@ -383,7 +385,12 @@ impl<'ccx, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'ccx, 'tcx> {
|
||||||
pub struct BoundsChecker<'cx,'tcx:'cx> {
|
pub struct BoundsChecker<'cx,'tcx:'cx> {
|
||||||
fcx: &'cx FnCtxt<'cx,'tcx>,
|
fcx: &'cx FnCtxt<'cx,'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
scope: region::CodeExtent,
|
|
||||||
|
// This field is often attached to item impls; it is not clear
|
||||||
|
// that `CodeExtent` is well-defined for such nodes, so pnkfelix
|
||||||
|
// has left it as a NodeId rather than porting to CodeExtent.
|
||||||
|
scope: ast::NodeId,
|
||||||
|
|
||||||
binding_count: uint,
|
binding_count: uint,
|
||||||
cache: Option<&'cx mut HashSet<Ty<'tcx>>>,
|
cache: Option<&'cx mut HashSet<Ty<'tcx>>>,
|
||||||
}
|
}
|
||||||
|
@ -391,7 +398,7 @@ pub struct BoundsChecker<'cx,'tcx:'cx> {
|
||||||
impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
|
impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
|
||||||
pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>,
|
pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
scope: region::CodeExtent,
|
scope: ast::NodeId,
|
||||||
cache: Option<&'cx mut HashSet<Ty<'tcx>>>)
|
cache: Option<&'cx mut HashSet<Ty<'tcx>>>)
|
||||||
-> BoundsChecker<'cx,'tcx> {
|
-> BoundsChecker<'cx,'tcx> {
|
||||||
BoundsChecker { fcx: fcx, span: span, scope: scope,
|
BoundsChecker { fcx: fcx, span: span, scope: scope,
|
||||||
|
@ -446,9 +453,12 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
|
||||||
where T : TypeFoldable<'tcx> + Repr<'tcx>
|
where T : TypeFoldable<'tcx> + Repr<'tcx>
|
||||||
{
|
{
|
||||||
self.binding_count += 1;
|
self.binding_count += 1;
|
||||||
let value = liberate_late_bound_regions(self.fcx.tcx(), self.scope, binder);
|
let value = liberate_late_bound_regions(
|
||||||
debug!("BoundsChecker::fold_binder: late-bound regions replaced: {}",
|
self.fcx.tcx(),
|
||||||
value.repr(self.tcx()));
|
region::DestructionScopeData::new(self.scope),
|
||||||
|
binder);
|
||||||
|
debug!("BoundsChecker::fold_binder: late-bound regions replaced: {} at scope: {:?}",
|
||||||
|
value.repr(self.tcx()), self.scope);
|
||||||
let value = value.fold_with(self);
|
let value = value.fold_with(self);
|
||||||
self.binding_count -= 1;
|
self.binding_count -= 1;
|
||||||
ty::Binder(value)
|
ty::Binder(value)
|
||||||
|
|
|
@ -1564,7 +1564,7 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
|
||||||
_ => typ,
|
_ => typ,
|
||||||
};
|
};
|
||||||
|
|
||||||
let body_scope = region::CodeExtent::from_node_id(body_id);
|
let body_scope = region::DestructionScopeData::new(body_id);
|
||||||
|
|
||||||
// "Required type" comes from the trait definition. It may
|
// "Required type" comes from the trait definition. It may
|
||||||
// contain late-bound regions from the method, but not the
|
// contain late-bound regions from the method, but not the
|
||||||
|
@ -1608,7 +1608,7 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
|
||||||
|
|
||||||
fn liberate_early_bound_regions<'tcx,T>(
|
fn liberate_early_bound_regions<'tcx,T>(
|
||||||
tcx: &ty::ctxt<'tcx>,
|
tcx: &ty::ctxt<'tcx>,
|
||||||
scope: region::CodeExtent,
|
scope: region::DestructionScopeData,
|
||||||
value: &T)
|
value: &T)
|
||||||
-> T
|
-> T
|
||||||
where T : TypeFoldable<'tcx> + Repr<'tcx>
|
where T : TypeFoldable<'tcx> + Repr<'tcx>
|
||||||
|
|
|
@ -50,7 +50,8 @@
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use std::old_io as io;
|
//! use std::old_io as io;
|
||||||
//!
|
//!
|
||||||
//! for line in io::stdin().lock().lines() {
|
//! let mut stdin = io::stdin();
|
||||||
|
//! for line in stdin.lock().lines() {
|
||||||
//! print!("{}", line.unwrap());
|
//! print!("{}", line.unwrap());
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
|
@ -143,7 +143,8 @@ impl StdinReader {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::old_io;
|
/// use std::old_io;
|
||||||
///
|
///
|
||||||
/// for line in old_io::stdin().lock().lines() {
|
/// let mut stdin = old_io::stdin();
|
||||||
|
/// for line in stdin.lock().lines() {
|
||||||
/// println!("{}", line.unwrap());
|
/// println!("{}", line.unwrap());
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -19,5 +19,6 @@ pub type header_map = HashMap<String, Rc<RefCell<Vec<Rc<String>>>>>;
|
||||||
|
|
||||||
// the unused ty param is necessary so this gets monomorphized
|
// the unused ty param is necessary so this gets monomorphized
|
||||||
pub fn request<T>(req: &header_map) {
|
pub fn request<T>(req: &header_map) {
|
||||||
let _x = req["METHOD".to_string()].clone().borrow().clone()[0].clone();
|
let data = req["METHOD".to_string()].clone();
|
||||||
|
let _x = data.borrow().clone()[0].clone();
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,7 +295,9 @@ fn main() {
|
||||||
let fd = std::old_io::File::open(&Path::new("shootout-k-nucleotide.data"));
|
let fd = std::old_io::File::open(&Path::new("shootout-k-nucleotide.data"));
|
||||||
get_sequence(&mut std::old_io::BufferedReader::new(fd), ">THREE")
|
get_sequence(&mut std::old_io::BufferedReader::new(fd), ">THREE")
|
||||||
} else {
|
} else {
|
||||||
get_sequence(&mut *std::old_io::stdin().lock(), ">THREE")
|
let mut stdin = std::old_io::stdin();
|
||||||
|
let mut stdin = stdin.lock();
|
||||||
|
get_sequence(&mut *stdin, ">THREE")
|
||||||
};
|
};
|
||||||
let input = Arc::new(input);
|
let input = Arc::new(input);
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,9 @@ fn main() {
|
||||||
let mut sudoku = if use_default {
|
let mut sudoku = if use_default {
|
||||||
Sudoku::from_vec(&DEFAULT_SUDOKU)
|
Sudoku::from_vec(&DEFAULT_SUDOKU)
|
||||||
} else {
|
} else {
|
||||||
Sudoku::read(&mut *old_io::stdin().lock())
|
let mut stdin = old_io::stdin();
|
||||||
|
let mut stdin = stdin.lock();
|
||||||
|
Sudoku::read(&mut *stdin)
|
||||||
};
|
};
|
||||||
sudoku.solve();
|
sudoku.solve();
|
||||||
sudoku.write(&mut old_io::stdout());
|
sudoku.write(&mut old_io::stdout());
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Tests the new destructor semantics.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let b = {
|
||||||
|
let a = Box::new(RefCell::new(4i8));
|
||||||
|
*a.borrow() + 1i8 //~ ERROR `*a` does not live long enough
|
||||||
|
};
|
||||||
|
println!("{}", b);
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Reject mixing cyclic structure and Drop when using fixed length
|
||||||
|
// arrays.
|
||||||
|
//
|
||||||
|
// (Compare against compile-fail/dropck_vec_cycle_checked.rs)
|
||||||
|
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use id::Id;
|
||||||
|
|
||||||
|
mod s {
|
||||||
|
#![allow(unstable)]
|
||||||
|
use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
|
||||||
|
|
||||||
|
static S_COUNT: AtomicUint = ATOMIC_UINT_INIT;
|
||||||
|
|
||||||
|
pub fn next_count() -> usize {
|
||||||
|
S_COUNT.fetch_add(1, Ordering::SeqCst) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod id {
|
||||||
|
use s;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Id {
|
||||||
|
orig_count: usize,
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
pub fn new() -> Id {
|
||||||
|
let c = s::next_count();
|
||||||
|
println!("building Id {}", c);
|
||||||
|
Id { orig_count: c, count: c }
|
||||||
|
}
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
println!("Id::count on {} returns {}", self.orig_count, self.count);
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Id {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropping Id {}", self.count);
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait HasId {
|
||||||
|
fn count(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CheckId<T:HasId> {
|
||||||
|
v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn CheckId<T:HasId>(t: T) -> CheckId<T> { CheckId{ v: t } }
|
||||||
|
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T:HasId> Drop for CheckId<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(self.v.count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct B<'a> {
|
||||||
|
id: Id,
|
||||||
|
a: [CheckId<Cell<Option<&'a B<'a>>>>; 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> HasId for Cell<Option<&'a B<'a>>> {
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
match self.get() {
|
||||||
|
None => 1,
|
||||||
|
Some(b) => b.id.count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> B<'a> {
|
||||||
|
fn new() -> B<'a> {
|
||||||
|
B { id: Id::new(), a: [CheckId(Cell::new(None)), CheckId(Cell::new(None))] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let (b1, b2, b3);
|
||||||
|
b1 = B::new();
|
||||||
|
b2 = B::new();
|
||||||
|
b3 = B::new();
|
||||||
|
b1.a[0].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough
|
||||||
|
b1.a[1].v.set(Some(&b3)); //~ ERROR `b3` does not live long enough
|
||||||
|
b2.a[0].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough
|
||||||
|
b2.a[1].v.set(Some(&b3)); //~ ERROR `b3` does not live long enough
|
||||||
|
b3.a[0].v.set(Some(&b1)); //~ ERROR `b1` does not live long enough
|
||||||
|
b3.a[1].v.set(Some(&b2)); //~ ERROR `b2` does not live long enough
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// A simple example of an unsound mixing of cyclic structure and Drop.
|
||||||
|
//
|
||||||
|
// Each `D` has a name and an optional reference to another `D`
|
||||||
|
// sibling, but also implements a drop method that prints out its own
|
||||||
|
// name as well as the name of its sibling.
|
||||||
|
//
|
||||||
|
// By setting up a cyclic structure, the drop code cannot possibly
|
||||||
|
// work. Therefore this code must be rejected.
|
||||||
|
//
|
||||||
|
// (As it turns out, essentially any attempt to install a sibling here
|
||||||
|
// will be rejected, regardless of whether it forms a cyclic
|
||||||
|
// structure or not. This is because the use of the same lifetime
|
||||||
|
// `'a` in `&'a D<'a>` cannot be satisfied when `D<'a>` implements
|
||||||
|
// `Drop`.)
|
||||||
|
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
struct D<'a> {
|
||||||
|
name: String,
|
||||||
|
p: Cell<Option<&'a D<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> D<'a> {
|
||||||
|
fn new(name: String) -> D<'a> { D { name: name, p: Cell::new(None) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<'a> Drop for D<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropping {} whose sibling is {:?}",
|
||||||
|
self.name, self.p.get().map(|d| &d.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn g() {
|
||||||
|
let (d1, d2) = (D::new(format!("d1")), D::new(format!("d2")));
|
||||||
|
d1.p.set(Some(&d2)); //~ ERROR `d2` does not live long enough
|
||||||
|
d2.p.set(Some(&d1)); //~ ERROR `d1` does not live long enough
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
g();
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Reject mixing cyclic structure and Drop when using TypedArena.
|
||||||
|
//
|
||||||
|
// (Compare against compile-fail/dropck_vec_cycle_checked.rs)
|
||||||
|
//
|
||||||
|
// (Also compare against compile-fail/dropck_tarena_unsound_drop.rs,
|
||||||
|
// which is a reduction of this code to more directly show the reason
|
||||||
|
// for the error message we see here.)
|
||||||
|
|
||||||
|
#![allow(unstable)]
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
extern crate arena;
|
||||||
|
|
||||||
|
use arena::TypedArena;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use id::Id;
|
||||||
|
|
||||||
|
mod s {
|
||||||
|
#![allow(unstable)]
|
||||||
|
use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
|
||||||
|
|
||||||
|
static S_COUNT: AtomicUint = ATOMIC_UINT_INIT;
|
||||||
|
|
||||||
|
pub fn next_count() -> usize {
|
||||||
|
S_COUNT.fetch_add(1, Ordering::SeqCst) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod id {
|
||||||
|
use s;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Id {
|
||||||
|
orig_count: usize,
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
pub fn new() -> Id {
|
||||||
|
let c = s::next_count();
|
||||||
|
println!("building Id {}", c);
|
||||||
|
Id { orig_count: c, count: c }
|
||||||
|
}
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
println!("Id::count on {} returns {}", self.orig_count, self.count);
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Id {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropping Id {}", self.count);
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait HasId {
|
||||||
|
fn count(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CheckId<T:HasId> {
|
||||||
|
v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn CheckId<T:HasId>(t: T) -> CheckId<T> { CheckId{ v: t } }
|
||||||
|
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T:HasId> Drop for CheckId<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(self.v.count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct C<'a> {
|
||||||
|
id: Id,
|
||||||
|
v: Vec<CheckId<Cell<Option<&'a C<'a>>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> HasId for Cell<Option<&'a C<'a>>> {
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
match self.get() {
|
||||||
|
None => 1,
|
||||||
|
Some(c) => c.id.count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> C<'a> {
|
||||||
|
fn new() -> C<'a> {
|
||||||
|
C { id: Id::new(), v: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f<'a>(arena: &'a TypedArena<C<'a>>) {
|
||||||
|
let c1 = arena.alloc(C::new());
|
||||||
|
let c2 = arena.alloc(C::new());
|
||||||
|
let c3 = arena.alloc(C::new());
|
||||||
|
|
||||||
|
c1.v.push(CheckId(Cell::new(None)));
|
||||||
|
c1.v.push(CheckId(Cell::new(None)));
|
||||||
|
c2.v.push(CheckId(Cell::new(None)));
|
||||||
|
c2.v.push(CheckId(Cell::new(None)));
|
||||||
|
c3.v.push(CheckId(Cell::new(None)));
|
||||||
|
c3.v.push(CheckId(Cell::new(None)));
|
||||||
|
|
||||||
|
c1.v[0].v.set(Some(c2));
|
||||||
|
c1.v[1].v.set(Some(c3));
|
||||||
|
c2.v[0].v.set(Some(c2));
|
||||||
|
c2.v[1].v.set(Some(c3));
|
||||||
|
c3.v[0].v.set(Some(c1));
|
||||||
|
c3.v[1].v.set(Some(c2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arena = TypedArena::new();
|
||||||
|
f(&arena); //~ ERROR `arena` does not live long enough
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Check that a arena (TypedArena) cannot carry elements whose drop
|
||||||
|
// methods might access borrowed data of lifetime that does not
|
||||||
|
// strictly outlive the arena itself.
|
||||||
|
//
|
||||||
|
// Compare against run-pass/dropck_tarena_sound_drop.rs, which shows a
|
||||||
|
// similar setup, but loosens `f` so that the struct `C<'a>` can be
|
||||||
|
// fed a lifetime longer than that of the arena.
|
||||||
|
//
|
||||||
|
// (Also compare against dropck_tarena_cycle_checked.rs, from which
|
||||||
|
// this was reduced to better understand its error message.)
|
||||||
|
|
||||||
|
#![allow(unstable)]
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
extern crate arena;
|
||||||
|
|
||||||
|
use arena::TypedArena;
|
||||||
|
|
||||||
|
trait HasId { fn count(&self) -> usize; }
|
||||||
|
|
||||||
|
struct CheckId<T:HasId> { v: T }
|
||||||
|
|
||||||
|
// In the code below, the impl of HasId for `&'a usize` does not
|
||||||
|
// actually access the borrowed data, but the point is that the
|
||||||
|
// interface to CheckId does not (and cannot) know that, and therefore
|
||||||
|
// when encountering the a value V of type CheckId<S>, we must
|
||||||
|
// conservatively force the type S to strictly outlive V.
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T:HasId> Drop for CheckId<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(self.v.count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct C<'a> { v: CheckId<&'a usize>, }
|
||||||
|
|
||||||
|
impl<'a> HasId for &'a usize { fn count(&self) -> usize { 1 } }
|
||||||
|
|
||||||
|
fn f<'a>(_arena: &'a TypedArena<C<'a>>) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arena: TypedArena<C> = TypedArena::new();
|
||||||
|
f(&arena); //~ ERROR `arena` does not live long enough
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Reject mixing cyclic structure and Drop when using Vec.
|
||||||
|
//
|
||||||
|
// (Compare against compile-fail/dropck_arr_cycle_checked.rs)
|
||||||
|
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use id::Id;
|
||||||
|
|
||||||
|
mod s {
|
||||||
|
#![allow(unstable)]
|
||||||
|
use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
|
||||||
|
|
||||||
|
static S_COUNT: AtomicUint = ATOMIC_UINT_INIT;
|
||||||
|
|
||||||
|
pub fn next_count() -> usize {
|
||||||
|
S_COUNT.fetch_add(1, Ordering::SeqCst) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod id {
|
||||||
|
use s;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Id {
|
||||||
|
orig_count: usize,
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
pub fn new() -> Id {
|
||||||
|
let c = s::next_count();
|
||||||
|
println!("building Id {}", c);
|
||||||
|
Id { orig_count: c, count: c }
|
||||||
|
}
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
println!("Id::count on {} returns {}", self.orig_count, self.count);
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Id {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropping Id {}", self.count);
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait HasId {
|
||||||
|
fn count(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CheckId<T:HasId> {
|
||||||
|
v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn CheckId<T:HasId>(t: T) -> CheckId<T> { CheckId{ v: t } }
|
||||||
|
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T:HasId> Drop for CheckId<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(self.v.count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct C<'a> {
|
||||||
|
id: Id,
|
||||||
|
v: Vec<CheckId<Cell<Option<&'a C<'a>>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> HasId for Cell<Option<&'a C<'a>>> {
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
match self.get() {
|
||||||
|
None => 1,
|
||||||
|
Some(c) => c.id.count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> C<'a> {
|
||||||
|
fn new() -> C<'a> {
|
||||||
|
C { id: Id::new(), v: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let (mut c1, mut c2, mut c3);
|
||||||
|
c1 = C::new();
|
||||||
|
c2 = C::new();
|
||||||
|
c3 = C::new();
|
||||||
|
|
||||||
|
c1.v.push(CheckId(Cell::new(None)));
|
||||||
|
c1.v.push(CheckId(Cell::new(None)));
|
||||||
|
c2.v.push(CheckId(Cell::new(None)));
|
||||||
|
c2.v.push(CheckId(Cell::new(None)));
|
||||||
|
c3.v.push(CheckId(Cell::new(None)));
|
||||||
|
c3.v.push(CheckId(Cell::new(None)));
|
||||||
|
|
||||||
|
c1.v[0].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough
|
||||||
|
c1.v[1].v.set(Some(&c3)); //~ ERROR `c3` does not live long enough
|
||||||
|
c2.v[0].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough
|
||||||
|
c2.v[1].v.set(Some(&c3)); //~ ERROR `c3` does not live long enough
|
||||||
|
c3.v[0].v.set(Some(&c1)); //~ ERROR `c1` does not live long enough
|
||||||
|
c3.v[1].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Checking that `Vec<T>` cannot hide lifetimes within `T` when `T`
|
||||||
|
// implements `Drop` and might access methods of values that have
|
||||||
|
// since been deallocated.
|
||||||
|
//
|
||||||
|
// In this case, the values in question hold (non-zero) unique-ids
|
||||||
|
// that zero themselves out when dropped, and are wrapped in another
|
||||||
|
// type with a destructor that asserts that the ids it references are
|
||||||
|
// indeed non-zero (i.e., effectively checking that the id's are not
|
||||||
|
// dropped while there are still any outstanding references).
|
||||||
|
//
|
||||||
|
// However, the values in question are also formed into a
|
||||||
|
// cyclic-structure, ensuring that there is no way for all of the
|
||||||
|
// conditions above to be satisfied, meaning that if the dropck is
|
||||||
|
// sound, it should reject this code.
|
||||||
|
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use id::Id;
|
||||||
|
|
||||||
|
mod s {
|
||||||
|
#![allow(unstable)]
|
||||||
|
use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
|
||||||
|
|
||||||
|
static S_COUNT: AtomicUint = ATOMIC_UINT_INIT;
|
||||||
|
|
||||||
|
/// generates globally unique count (global across the current
|
||||||
|
/// process, that is)
|
||||||
|
pub fn next_count() -> usize {
|
||||||
|
S_COUNT.fetch_add(1, Ordering::SeqCst) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod id {
|
||||||
|
use s;
|
||||||
|
|
||||||
|
/// Id represents a globally unique identifier (global across the
|
||||||
|
/// current process, that is). When dropped, it automatically
|
||||||
|
/// clears its `count` field, but leaves `orig_count` untouched,
|
||||||
|
/// so that if there are subsequent (erroneous) invocations of its
|
||||||
|
/// method (which is unsound), we can observe it by seeing that
|
||||||
|
/// the `count` is 0 while the `orig_count` is non-zero.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Id {
|
||||||
|
orig_count: usize,
|
||||||
|
count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
/// Creates an `Id` with a globally unique count.
|
||||||
|
pub fn new() -> Id {
|
||||||
|
let c = s::next_count();
|
||||||
|
println!("building Id {}", c);
|
||||||
|
Id { orig_count: c, count: c }
|
||||||
|
}
|
||||||
|
/// returns the `count` of self; should be non-zero if
|
||||||
|
/// everything is working.
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
println!("Id::count on {} returns {}", self.orig_count, self.count);
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Id {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropping Id {}", self.count);
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait HasId {
|
||||||
|
fn count(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CheckId<T:HasId> {
|
||||||
|
v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn CheckId<T:HasId>(t: T) -> CheckId<T> { CheckId{ v: t } }
|
||||||
|
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T:HasId> Drop for CheckId<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(self.v.count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct C<'a> {
|
||||||
|
id: Id,
|
||||||
|
v: Vec<CheckId<Cell<Option<&'a C<'a>>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> HasId for Cell<Option<&'a C<'a>>> {
|
||||||
|
fn count(&self) -> usize {
|
||||||
|
match self.get() {
|
||||||
|
None => 1,
|
||||||
|
Some(c) => c.id.count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> C<'a> {
|
||||||
|
fn new() -> C<'a> {
|
||||||
|
C { id: Id::new(), v: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let (mut c1, mut c2);
|
||||||
|
c1 = C::new();
|
||||||
|
c2 = C::new();
|
||||||
|
|
||||||
|
c1.v.push(CheckId(Cell::new(None)));
|
||||||
|
c2.v.push(CheckId(Cell::new(None)));
|
||||||
|
c1.v[0].v.set(Some(&c2)); //~ ERROR `c2` does not live long enough
|
||||||
|
c2.v[0].v.set(Some(&c1)); //~ ERROR `c1` does not live long enough
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// This test is a simple example of code that violates the dropck
|
||||||
|
// rules: it pushes `&x` and `&y` into `v`, but the referenced data
|
||||||
|
// will be dropped before the vector itself is.
|
||||||
|
|
||||||
|
// (In principle we know that `Vec` does not reference the data it
|
||||||
|
// owns from within its drop code, apart from calling drop on each
|
||||||
|
// element it owns; thus, for data like this, it seems like we could
|
||||||
|
// loosen the restrictions here if we wanted. But it also is not
|
||||||
|
// clear whether such loosening is terribly important.)
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
|
||||||
|
let x: i8 = 3;
|
||||||
|
let y: i8 = 4;
|
||||||
|
|
||||||
|
v.push(&x); //~ ERROR `x` does not live long enough
|
||||||
|
v.push(&y); //~ ERROR `y` does not live long enough
|
||||||
|
|
||||||
|
assert_eq!(v.as_slice(), [&3, &4]);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
#[derive(Show)]
|
||||||
|
struct B<'a> {
|
||||||
|
a: [Cell<Option<&'a B<'a>>>; 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> B<'a> {
|
||||||
|
fn new() -> B<'a> {
|
||||||
|
B { a: [Cell::new(None), Cell::new(None)] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let (b1, b2, b3);
|
||||||
|
b1 = B::new();
|
||||||
|
b2 = B::new();
|
||||||
|
b3 = B::new();
|
||||||
|
b1.a[0].set(Some(&b2));
|
||||||
|
b1.a[1].set(Some(&b3));
|
||||||
|
b2.a[0].set(Some(&b2));
|
||||||
|
b2.a[1].set(Some(&b3));
|
||||||
|
b3.a[0].set(Some(&b1));
|
||||||
|
b3.a[1].set(Some(&b2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
}
|
|
@ -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.
|
||||||
|
|
||||||
|
// Check that a arena (TypedArena) can carry elements whose drop
|
||||||
|
// methods might access borrowed data, as long as the borrowed data
|
||||||
|
// has lifetime that strictly outlives the arena itself.
|
||||||
|
//
|
||||||
|
// Compare against compile-fail/dropck_tarena_unsound_drop.rs, which
|
||||||
|
// shows a similar setup, but restricts `f` so that the struct `C<'a>`
|
||||||
|
// is force-fed a lifetime equal to that of the borrowed arena.
|
||||||
|
|
||||||
|
#![allow(unstable)]
|
||||||
|
#![feature(unsafe_destructor)]
|
||||||
|
|
||||||
|
extern crate arena;
|
||||||
|
|
||||||
|
use arena::TypedArena;
|
||||||
|
|
||||||
|
trait HasId { fn count(&self) -> usize; }
|
||||||
|
|
||||||
|
struct CheckId<T:HasId> { v: T }
|
||||||
|
|
||||||
|
// In the code below, the impl of HasId for `&'a usize` does not
|
||||||
|
// actually access the borrowed data, but the point is that the
|
||||||
|
// interface to CheckId does not (and cannot) know that, and therefore
|
||||||
|
// when encountering the a value V of type CheckId<S>, we must
|
||||||
|
// conservatively force the type S to strictly outlive V.
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T:HasId> Drop for CheckId<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
assert!(self.v.count() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct C<'a> { _v: CheckId<&'a usize>, }
|
||||||
|
|
||||||
|
impl<'a> HasId for &'a usize { fn count(&self) -> usize { 1 } }
|
||||||
|
|
||||||
|
fn f<'a, 'b>(_arena: &'a TypedArena<C<'b>>) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arena: TypedArena<C> = TypedArena::new();
|
||||||
|
f(&arena);
|
||||||
|
}
|
|
@ -37,7 +37,8 @@ fn parent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn child() {
|
fn child() {
|
||||||
for line in old_io::stdin().lock().lines() {
|
let mut stdin = old_io::stdin();
|
||||||
|
for line in stdin.lock().lines() {
|
||||||
println!("{}", line.unwrap());
|
println!("{}", line.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ fn main() {
|
||||||
fn child() {
|
fn child() {
|
||||||
old_io::stdout().write_line("foo").unwrap();
|
old_io::stdout().write_line("foo").unwrap();
|
||||||
old_io::stderr().write_line("bar").unwrap();
|
old_io::stderr().write_line("bar").unwrap();
|
||||||
assert_eq!(old_io::stdin().lock().read_line().err().unwrap().kind, old_io::EndOfFile);
|
let mut stdin = old_io::stdin();
|
||||||
|
assert_eq!(stdin.lock().read_line().err().unwrap().kind, old_io::EndOfFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
|
|
@ -29,5 +29,5 @@ impl Bar {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let b = RefCell::new(Bar);
|
let b = RefCell::new(Bar);
|
||||||
b.borrow().foo()
|
b.borrow().foo();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
struct C<'a> {
|
||||||
|
p: Cell<Option<&'a C<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> C<'a> {
|
||||||
|
fn new() -> C<'a> { C { p: Cell::new(None) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f1() {
|
||||||
|
let (c1, c2) = (C::new(), C::new());
|
||||||
|
c1.p.set(Some(&c2));
|
||||||
|
c2.p.set(Some(&c1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f2() {
|
||||||
|
let (c1, c2);
|
||||||
|
c1 = C::new();
|
||||||
|
c2 = C::new();
|
||||||
|
c1.p.set(Some(&c2));
|
||||||
|
c2.p.set(Some(&c1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f1();
|
||||||
|
f2();
|
||||||
|
}
|
|
@ -22,8 +22,9 @@ struct Point {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
let box_5 = box 5u;
|
||||||
assert_eq!(Rc::new(5u).to_uint(), Some(5));
|
assert_eq!(Rc::new(5u).to_uint(), Some(5));
|
||||||
assert_eq!((box &box &Rc::new(box box &box 5u)).to_uint(), Some(5));
|
assert_eq!((box &box &Rc::new(box box &box_5)).to_uint(), Some(5));
|
||||||
let point = Rc::new(Point {x: 2, y: 4});
|
let point = Rc::new(Point {x: 2, y: 4});
|
||||||
assert_eq!(point.x, 2);
|
assert_eq!(point.x, 2);
|
||||||
assert_eq!(point.y, 4);
|
assert_eq!(point.y, 4);
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// This is a regression test for something that only came up while
|
||||||
|
// attempting to bootstrap librustc with new destructor lifetime
|
||||||
|
// semantics.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
// This version does not yet work (associated type issues)...
|
||||||
|
#[cfg(cannot_use_this_yet)]
|
||||||
|
fn foo<'a>(map: RefCell<HashMap<&'static str, &'a [u8]>>) {
|
||||||
|
let one = [1u];
|
||||||
|
assert_eq!(map.borrow().get("one"), Some(&one[]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(cannot_use_this_yet_either)]
|
||||||
|
// ... and this version does not work (the lifetime of `one` is
|
||||||
|
// supposed to match the lifetime `'a`) ...
|
||||||
|
fn foo<'a>(map: RefCell<HashMap<&'static str, &'a [u8]>>) {
|
||||||
|
let one = [1u];
|
||||||
|
assert_eq!(map.borrow().get("one"), Some(&one.as_slice()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(not(cannot_use_this_yet),not(cannot_use_this_yet_either)))]
|
||||||
|
fn foo<'a>(map: RefCell<HashMap<&'static str, &'a [u8]>>) {
|
||||||
|
// ...so instead we walk through the trivial slice and make sure
|
||||||
|
// it contains the element we expect.
|
||||||
|
|
||||||
|
for (i, &x) in map.borrow().get("one").unwrap().iter().enumerate() {
|
||||||
|
assert_eq!((i, x), (0, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let zer = [0u8];
|
||||||
|
let one = [1u8];
|
||||||
|
let two = [2u8];
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("zero", &zer[]);
|
||||||
|
map.insert("one", &one[]);
|
||||||
|
map.insert("two", &two[]);
|
||||||
|
let map = RefCell::new(map);
|
||||||
|
foo(map);
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// This is a regression test for something that only came up while
|
||||||
|
// attempting to bootstrap libsyntax; it is adapted from
|
||||||
|
// `syntax::ext::tt::generic_extension`.
|
||||||
|
|
||||||
|
pub struct E<'a> {
|
||||||
|
pub f: &'a u8,
|
||||||
|
}
|
||||||
|
impl<'b> E<'b> {
|
||||||
|
pub fn m(&self) -> &'b u8 { self.f }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct P<'c> {
|
||||||
|
pub g: &'c u8,
|
||||||
|
}
|
||||||
|
pub trait M {
|
||||||
|
fn n(&self) -> u8;
|
||||||
|
}
|
||||||
|
impl<'d> M for P<'d> {
|
||||||
|
fn n(&self) -> u8 { *self.g }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extension<'e>(x: &'e E<'e>) -> Box<M+'e> {
|
||||||
|
loop {
|
||||||
|
let p = P { g: x.m() };
|
||||||
|
return Box::new(p) as Box<M+'e>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let w = E { f: &10u8 };
|
||||||
|
let o = extension(&w);
|
||||||
|
assert_eq!(o.n(), 10u8);
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Uncovered during work on new scoping rules for safe destructors
|
||||||
|
// as an important use case to support properly.
|
||||||
|
|
||||||
|
pub struct E<'a> {
|
||||||
|
pub f: &'a u8,
|
||||||
|
}
|
||||||
|
impl<'b> E<'b> {
|
||||||
|
pub fn m(&self) -> &'b u8 { self.f }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct P<'c> {
|
||||||
|
pub g: &'c u8,
|
||||||
|
}
|
||||||
|
pub trait M {
|
||||||
|
fn n(&self) -> u8;
|
||||||
|
}
|
||||||
|
impl<'d> M for P<'d> {
|
||||||
|
fn n(&self) -> u8 { *self.g }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extension<'e>(x: &'e E<'e>) -> Box<M+'e> {
|
||||||
|
loop {
|
||||||
|
let p = P { g: x.m() };
|
||||||
|
return Box::new(p) as Box<M+'e>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let w = E { f: &10u8 };
|
||||||
|
let o = extension(&w);
|
||||||
|
assert_eq!(o.n(), 10u8);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
#[derive(Show)]
|
||||||
|
struct C<'a> {
|
||||||
|
v: Vec<Cell<Option<&'a C<'a>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> C<'a> {
|
||||||
|
fn new() -> C<'a> {
|
||||||
|
C { v: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let (mut c1, mut c2, mut c3);
|
||||||
|
c1 = C::new();
|
||||||
|
c2 = C::new();
|
||||||
|
c3 = C::new();
|
||||||
|
|
||||||
|
c1.v.push(Cell::new(None));
|
||||||
|
c1.v.push(Cell::new(None));
|
||||||
|
c2.v.push(Cell::new(None));
|
||||||
|
c2.v.push(Cell::new(None));
|
||||||
|
c3.v.push(Cell::new(None));
|
||||||
|
c3.v.push(Cell::new(None));
|
||||||
|
|
||||||
|
c1.v[0].set(Some(&c2));
|
||||||
|
c1.v[1].set(Some(&c3));
|
||||||
|
c2.v[0].set(Some(&c2));
|
||||||
|
c2.v[1].set(Some(&c3));
|
||||||
|
c3.v[0].set(Some(&c1));
|
||||||
|
c3.v[1].set(Some(&c2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
#[derive(Show)]
|
||||||
|
struct Refs<'a> {
|
||||||
|
v: Vec<Cell<Option<&'a C<'a>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Show)]
|
||||||
|
struct C<'a> {
|
||||||
|
refs: Refs<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Refs<'a> {
|
||||||
|
fn new() -> Refs<'a> {
|
||||||
|
Refs { v: Vec::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> C<'a> {
|
||||||
|
fn new() -> C<'a> {
|
||||||
|
C { refs: Refs::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
let (mut c1, mut c2, mut c3);
|
||||||
|
c1 = C::new();
|
||||||
|
c2 = C::new();
|
||||||
|
c3 = C::new();
|
||||||
|
|
||||||
|
c1.refs.v.push(Cell::new(None));
|
||||||
|
c1.refs.v.push(Cell::new(None));
|
||||||
|
c2.refs.v.push(Cell::new(None));
|
||||||
|
c2.refs.v.push(Cell::new(None));
|
||||||
|
c3.refs.v.push(Cell::new(None));
|
||||||
|
c3.refs.v.push(Cell::new(None));
|
||||||
|
|
||||||
|
c1.refs.v[0].set(Some(&c2));
|
||||||
|
c1.refs.v[1].set(Some(&c3));
|
||||||
|
c2.refs.v[0].set(Some(&c2));
|
||||||
|
c2.refs.v[1].set(Some(&c3));
|
||||||
|
c3.refs.v[0].set(Some(&c1));
|
||||||
|
c3.refs.v[1].set(Some(&c2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f();
|
||||||
|
}
|
Loading…
Reference in New Issue