about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-02-11 17:59:37 +0000
committerbors <bors@rust-lang.org>2015-02-11 17:59:37 +0000
commite29f42025513374f1a88404491d1b40386acf994 (patch)
treea6459e62987a6c3b80cdf7c9810c9405d1d1fe1c
parent446bc899b28e988f4252beca0d1858e7f7d866b1 (diff)
parent2c9d81b2d47d1c8d0e4c771b778238948c269c20 (diff)
downloadrust-e29f42025513374f1a88404491d1b40386acf994.tar.gz
rust-e29f42025513374f1a88404491d1b40386acf994.zip
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
-rw-r--r--src/libarena/lib.rs71
-rw-r--r--src/libcollections/btree/node.rs16
-rw-r--r--src/libcollections/vec.rs18
-rw-r--r--src/libcore/marker.rs18
-rw-r--r--src/libcore/ptr.rs17
-rw-r--r--src/libcoretest/ptr.rs2
-rw-r--r--src/libflate/lib.rs4
-rw-r--r--src/librustc/metadata/tydecode.rs11
-rw-r--r--src/librustc/metadata/tyencode.rs8
-rw-r--r--src/librustc/middle/astencode.rs6
-rw-r--r--src/librustc/middle/infer/error_reporting.rs27
-rw-r--r--src/librustc/middle/infer/mod.rs5
-rw-r--r--src/librustc/middle/infer/region_inference/mod.rs15
-rw-r--r--src/librustc/middle/lang_items.rs2
-rw-r--r--src/librustc/middle/liveness.rs4
-rw-r--r--src/librustc/middle/mem_categorization.rs5
-rw-r--r--src/librustc/middle/region.rs139
-rw-r--r--src/librustc/middle/resolve_lifetime.rs16
-rw-r--r--src/librustc/middle/ty.rs85
-rw-r--r--src/librustc/util/ppaux.rs19
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/mod.rs2
-rw-r--r--src/librustc_driver/test.rs4
-rw-r--r--src/librustc_resolve/lib.rs8
-rw-r--r--src/librustc_trans/trans/cleanup.rs17
-rw-r--r--src/librustc_typeck/check/closure.rs6
-rw-r--r--src/librustc_typeck/check/dropck.rs292
-rw-r--r--src/librustc_typeck/check/method/probe.rs3
-rw-r--r--src/librustc_typeck/check/mod.rs9
-rw-r--r--src/librustc_typeck/check/regionck.rs72
-rw-r--r--src/librustc_typeck/check/wf.rs32
-rw-r--r--src/librustc_typeck/collect.rs4
-rw-r--r--src/libstd/old_io/mod.rs3
-rw-r--r--src/libstd/old_io/stdio.rs3
-rw-r--r--src/test/auxiliary/issue-2631-a.rs3
-rw-r--r--src/test/bench/shootout-k-nucleotide.rs4
-rw-r--r--src/test/bench/sudoku.rs4
-rw-r--r--src/test/compile-fail/destructor-restrictions.rs21
-rw-r--r--src/test/compile-fail/dropck_arr_cycle_checked.rs115
-rw-r--r--src/test/compile-fail/dropck_direct_cycle_with_drop.rs55
-rw-r--r--src/test/compile-fail/dropck_tarena_cycle_checked.rs130
-rw-r--r--src/test/compile-fail/dropck_tarena_unsound_drop.rs54
-rw-r--r--src/test/compile-fail/dropck_vec_cycle_checked.rs122
-rw-r--r--src/test/compile-fail/vec-must-not-hide-type-from-dropck.rs135
-rw-r--r--src/test/compile-fail/vec_refs_data_with_early_death.rs31
-rw-r--r--src/test/run-pass/arr_cycle.rs39
-rw-r--r--src/test/run-pass/dropck_tarena_sound_drop.rs51
-rw-r--r--src/test/run-pass/issue-13304.rs3
-rw-r--r--src/test/run-pass/issue-14456.rs3
-rw-r--r--src/test/run-pass/multidispatch-conditional-impl-not-considered.rs2
-rw-r--r--src/test/run-pass/nondrop-cycle.rs38
-rw-r--r--src/test/run-pass/overloaded-autoderef.rs3
-rw-r--r--src/test/run-pass/regions-refcell.rs53
-rw-r--r--src/test/run-pass/regions-trait-object-1.rs43
-rw-r--r--src/test/run-pass/trait-object-with-lifetime-bound.rs42
-rw-r--r--src/test/run-pass/vec_cycle.rs47
-rw-r--r--src/test/run-pass/vec_cycle_wrapped.rs58
56 files changed, 1863 insertions, 136 deletions
diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs
index 223c5111f8f..4cd3d587580 100644
--- a/src/libarena/lib.rs
+++ b/src/libarena/lib.rs
@@ -42,6 +42,7 @@ use std::cell::{Cell, RefCell};
 use std::cmp;
 use std::intrinsics::{TyDesc, get_tydesc};
 use std::intrinsics;
+use std::marker;
 use std::mem;
 use std::num::{Int, UnsignedInt};
 use std::ptr;
@@ -88,27 +89,29 @@ impl Chunk {
 /// than objects without destructors. This reduces overhead when initializing
 /// plain-old-data (`Copy` types) and means we don't need to waste time running
 /// their destructors.
-pub struct Arena {
+pub struct Arena<'longer_than_self> {
     // The head is separated out from the list as a unbenchmarked
     // microoptimization, to avoid needing to case on the list to access the
     // head.
     head: RefCell<Chunk>,
     copy_head: RefCell<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.
-    pub fn new() -> Arena {
+    pub fn new() -> Arena<'a> {
         Arena::new_with_size(32)
     }
 
     /// 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 {
             head: RefCell::new(chunk(initial_size, false)),
             copy_head: RefCell::new(chunk(initial_size, true)),
             chunks: RefCell::new(Vec::new()),
+            _invariant: marker::InvariantLifetime,
         }
     }
 }
@@ -122,7 +125,7 @@ fn chunk(size: usize, is_copy: bool) -> Chunk {
 }
 
 #[unsafe_destructor]
-impl Drop for Arena {
+impl<'longer_than_self> Drop for Arena<'longer_than_self> {
     fn drop(&mut self) {
         unsafe {
             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)
 }
 
-impl Arena {
+impl<'longer_than_self> Arena<'longer_than_self> {
     fn chunk_size(&self) -> usize {
         self.copy_head.borrow().capacity()
     }
@@ -293,7 +296,7 @@ impl Arena {
     /// Allocates a new item in the arena, using `op` to initialize the value,
     /// and returns a reference to it.
     #[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 {
             if intrinsics::needs_drop::<T>() {
                 self.alloc_noncopy(op)
@@ -318,20 +321,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]
 #[should_fail]
 fn test_arena_destructors_fail() {
     let arena = Arena::new();
@@ -365,6 +354,10 @@ pub struct TypedArena<T> {
 
     /// A pointer to the first arena segment.
     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> {
@@ -460,6 +453,7 @@ impl<T> TypedArena<T> {
                 ptr: Cell::new((*chunk).start() as *const T),
                 end: Cell::new((*chunk).end() as *const T),
                 first: RefCell::new(chunk),
+                _own: marker::PhantomData,
             }
         }
     }
@@ -524,6 +518,41 @@ mod tests {
     }
 
     #[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]
     pub fn test_copy() {
         let arena = TypedArena::new();
         for _ in 0..100000 {
diff --git a/src/libcollections/btree/node.rs b/src/libcollections/btree/node.rs
index bfe74cc6fb4..24523d4dcc9 100644
--- a/src/libcollections/btree/node.rs
+++ b/src/libcollections/btree/node.rs
@@ -278,7 +278,7 @@ impl<T> Drop for RawItems<T> {
 #[unsafe_destructor]
 impl<K, V> Drop for Node<K, V> {
     fn drop(&mut self) {
-        if self.keys.0.is_null() {
+        if self.keys.ptr.is_null() {
             // We have already cleaned up this node.
             return;
         }
@@ -292,7 +292,7 @@ impl<K, V> Drop for Node<K, V> {
             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) {
         let (alignment, size) =
                 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]
     pub fn as_slices<'a>(&'a self) -> (&'a [K], &'a [V]) {
         unsafe {(
             mem::transmute(raw::Slice {
-                data: self.keys.0,
+                data: self.keys.ptr,
                 len: self.len()
             }),
             mem::transmute(raw::Slice {
-                data: self.vals.0,
+                data: self.vals.ptr,
                 len: self.len()
             })
         )}
@@ -368,7 +368,7 @@ impl<K, V> Node<K, V> {
         } else {
             unsafe {
                 mem::transmute(raw::Slice {
-                    data: self.edges.0,
+                    data: self.edges.ptr,
                     len: self.len() + 1
                 })
             }
@@ -586,7 +586,7 @@ impl <K, V> Node<K, V> {
 
     /// If the node has any children
     pub fn is_leaf(&self) -> bool {
-        self.edges.0.is_null()
+        self.edges.ptr.is_null()
     }
 
     /// if the node has too few elements
@@ -1064,7 +1064,7 @@ impl<K, V> Node<K, V> {
                     vals: RawItems::from_slice(self.vals()),
                     edges: RawItems::from_slice(self.edges()),
 
-                    ptr: self.keys.0 as *mut u8,
+                    ptr: self.keys.ptr as *mut u8,
                     capacity: self.capacity(),
                     is_leaf: self.is_leaf()
                 },
diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs
index 1cd2a89ad60..341d91538ad 100644
--- a/src/libcollections/vec.rs
+++ b/src/libcollections/vec.rs
@@ -57,7 +57,7 @@ use core::default::Default;
 use core::fmt;
 use core::hash::{self, Hash};
 use core::iter::{repeat, FromIterator, IntoIterator};
-use core::marker::{ContravariantLifetime, InvariantType};
+use core::marker::{self, ContravariantLifetime, InvariantType};
 use core::mem;
 use core::nonzero::NonZero;
 use core::num::{Int, UnsignedInt};
@@ -140,6 +140,7 @@ pub struct Vec<T> {
     ptr: NonZero<*mut T>,
     len: usize,
     cap: usize,
+    _own: marker::PhantomData<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
         // 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.
-        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.
@@ -198,7 +199,7 @@ impl<T> Vec<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with_capacity(capacity: usize) -> Vec<T> {
         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 {
             Vec::new()
         } else {
@@ -206,7 +207,7 @@ impl<T> Vec<T> {
                                .expect("capacity overflow");
             let ptr = unsafe { allocate(size, mem::min_align_of::<T>()) };
             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")]
     pub unsafe fn from_raw_parts(ptr: *mut T, length: usize,
                                  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.
@@ -1626,7 +1632,7 @@ impl<T> IntoIter<T> {
             for _x in self.by_ref() { }
             let IntoIter { allocation, cap, ptr: _ptr, end: _end } = self;
             mem::forget(self);
-            Vec { ptr: NonZero::new(allocation), cap: cap, len: 0 }
+            Vec::from_raw_parts(allocation, 0, cap)
         }
     }
 }
diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs
index bf9e6bccc71..da93d4f6ca4 100644
--- a/src/libcore/marker.rs
+++ b/src/libcore/marker.rs
@@ -202,6 +202,24 @@ pub unsafe trait Sync {
     // 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
 /// covariant with respect to the type itself. This is (typically)
diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs
index bf801a88ca5..1b8ec048f8d 100644
--- a/src/libcore/ptr.rs
+++ b/src/libcore/ptr.rs
@@ -92,7 +92,7 @@ use mem;
 use clone::Clone;
 use intrinsics;
 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::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
 /// internally use raw pointers to manage the memory that they own.
 #[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
 /// reference is unaliased. Note that this aliasing invariant is
@@ -550,6 +554,13 @@ impl<T> Unique<T> {
     #[unstable(feature = "core",
                reason = "recently added to this module")]
     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 }
+}
diff --git a/src/libcoretest/ptr.rs b/src/libcoretest/ptr.rs
index 2365b907b3f..797c150e859 100644
--- a/src/libcoretest/ptr.rs
+++ b/src/libcoretest/ptr.rs
@@ -172,7 +172,7 @@ fn test_set_memory() {
 fn test_unsized_unique() {
     let xs: &mut [_] = &mut [1, 2, 3];
     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];
     assert!(ys == zs);
 }
diff --git a/src/libflate/lib.rs b/src/libflate/lib.rs
index a81b8777af4..ff6400a11df 100644
--- a/src/libflate/lib.rs
+++ b/src/libflate/lib.rs
@@ -45,13 +45,13 @@ pub struct Bytes {
 impl Deref for Bytes {
     type Target = [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 {
     fn drop(&mut self) {
-        unsafe { libc::free(self.ptr.0 as *mut _); }
+        unsafe { libc::free(self.ptr.ptr as *mut _); }
     }
 }
 
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 7cc7e49b6d2..9962f49dfcf 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -349,7 +349,7 @@ fn parse_region_<F>(st: &mut PState, conv: &mut F) -> ty::Region where
       }
       'f' => {
         assert_eq!(next(st), '[');
-        let scope = parse_scope(st);
+        let scope = parse_destruction_scope_data(st);
         assert_eq!(next(st), '|');
         let br = parse_bound_region_(st, conv);
         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;
             region::CodeExtent::Misc(node_id)
         }
+        'D' => {
+            let node_id = parse_uint(st) as ast::NodeId;
+            region::CodeExtent::DestructionScope(node_id)
+        }
         'B' => {
             let node_id = parse_uint(st) as ast::NodeId;
             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
     F: FnOnce(&mut PState<'a, 'tcx>) -> T,
 {
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index f8081e2c309..640b9649286 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -251,7 +251,7 @@ pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) {
         }
         ty::ReFree(ref fr) => {
             mywrite!(w, "f[");
-            enc_scope(w, cx, fr.scope);
+            enc_destruction_scope_data(w, fr.scope);
             mywrite!(w, "|");
             enc_bound_region(w, cx, fr.bound_region);
             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::Remainder(region::BlockRemainder {
             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) {
     match br {
         ty::BrAnon(idx) => {
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index b0fe743b683..8e3bf0fa28d 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -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 {
     fn tr(&self, dcx: &DecodeContext) -> ty::BoundRegion {
         match *self {
diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs
index 99896535442..5d7a56ef0e6 100644
--- a/src/librustc/middle/infer/error_reporting.rs
+++ b/src/librustc/middle/infer/error_reporting.rs
@@ -242,7 +242,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     }
                 }
                 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) {
                         Some(ref same_frs) => {
                             var_origins.push(var_origin);
@@ -304,7 +304,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                         return None
                     }
                     assert!(fr1.scope == fr2.scope);
-                    (fr1.scope.node_id(), fr1, fr2)
+                    (fr1.scope.node_id, fr1, fr2)
                 },
                 _ => return None
             };
@@ -709,6 +709,23 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     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) => {
                 self.tcx.sess.span_err(
                     span,
@@ -1629,6 +1646,12 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
                     &format!("...so that the declared lifetime parameter bounds \
                                 are satisfied")[]);
             }
+            infer::SafeDestructor(span) => {
+                self.tcx.sess.span_note(
+                    span,
+                    "...so that references are valid when the destructor \
+                     runs")
+            }
         }
     }
 }
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index f8dae3e92da..41310f05588 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -215,6 +215,9 @@ pub enum SubregionOrigin<'tcx> {
 
     // An auto-borrow that does not enclose the expr where it occurs
     AutoBorrow(Span),
+
+    // Region constraint arriving from destructor safety
+    SafeDestructor(Span),
 }
 
 /// Times when we replace late-bound regions with variables:
@@ -1197,6 +1200,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
             CallReturn(a) => a,
             AddrOf(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)),
             AddrOf(a) => format!("AddrOf({})", a.repr(tcx)),
             AutoBorrow(a) => format!("AutoBorrow({})", a.repr(tcx)),
+            SafeDestructor(a) => format!("SafeDestructor({})", a.repr(tcx)),
         }
     }
 }
diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs
index 1a7308a4f18..3dba8045d60 100644
--- a/src/librustc/middle/infer/region_inference/mod.rs
+++ b/src/librustc/middle/infer/region_inference/mod.rs
@@ -760,11 +760,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
             // A "free" region can be interpreted as "some region
             // at least as big as the block fr.scope_id".  So, we can
             // 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
               // the scope region `s_id`, then the LUB is the free
               // 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,
               // 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
                 // is the scope `s_id`.  Otherwise, as we do not know
                 // big the free region is precisely, the GLB is undefined.
-                match self.tcx.region_maps.nearest_common_ancestor(fr.scope, s_id) {
-                    Some(r_id) if r_id == fr.scope => Ok(s),
+                let fr_scope = fr.scope.to_code_extent();
+                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))
                 }
             }
@@ -915,7 +917,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
                 Ok(ty::ReFree(*b))
             } else {
                 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 {
                 if !self.is_subregion_of(lower_bound.region,
                                          upper_bound.region) {
+                    debug!("pushing SubSupConflict sub: {:?} sup: {:?}",
+                           lower_bound.region, upper_bound.region);
                     errors.push(SubSupConflict(
                         (*self.var_origins.borrow())[node_idx.index as uint].clone(),
                         lower_bound.origin.clone(),
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index 05969d4ea43..ef72c2242c1 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -312,6 +312,8 @@ lets_do_this! {
     ExchangeHeapLangItem,            "exchange_heap",           exchange_heap;
     OwnedBoxLangItem,                "owned_box",               owned_box;
 
+    PhantomDataItem,                 "phantom_data",            phantom_data;
+
     CovariantTypeItem,               "covariant_type",          covariant_type;
     ContravariantTypeItem,           "contravariant_type",      contravariant_type;
     InvariantTypeItem,               "invariant_type",          invariant_type;
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index fcc5d70a7a5..d4fe0979313 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -112,7 +112,7 @@ use self::VarKind::*;
 use middle::def::*;
 use middle::mem_categorization::Typer;
 use middle::pat_util;
-use middle::region::CodeExtent;
+use middle::region;
 use middle::ty;
 use middle::ty::ClosureTyper;
 use lint;
@@ -1514,7 +1514,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         let fn_ret =
             ty::liberate_late_bound_regions(
                 self.ir.tcx,
-                CodeExtent::from_node_id(body.id),
+                region::DestructionScopeData::new(body.id),
                 &self.fn_ret(id));
 
         match fn_ret {
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 58492c817fd..fed2f7d9245 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -760,7 +760,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
 
         // Region of environment pointer
         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
         });
 
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 037b1c31a17..2f0462ab8c3 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -27,11 +27,66 @@ use syntax::{ast, visit};
 use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
 use syntax::ast_util::{stmt_id};
 use syntax::ast_map;
+use syntax::ptr::P;
 use syntax::visit::{Visitor, FnKind};
 
 /// CodeExtent represents a statically-describable extent that can be
 /// 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
 /// placate the same deriving in `ty::FreeRegion`, but we may want to
 /// actually attach a more meaningful ordering to scopes than the one
@@ -40,7 +95,24 @@ use syntax::visit::{Visitor, FnKind};
            RustcDecodable, Debug, Copy)]
 pub enum CodeExtent {
     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
@@ -82,6 +154,7 @@ impl CodeExtent {
         match *self {
             CodeExtent::Misc(node_id) => node_id,
             CodeExtent::Remainder(br) => br.block,
+            CodeExtent::DestructionScope(node_id) => node_id,
         }
     }
 
@@ -95,6 +168,8 @@ impl CodeExtent {
             CodeExtent::Remainder(br) =>
                 CodeExtent::Remainder(BlockRemainder {
                     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()) {
             Some(ast_map::NodeBlock(ref blk)) => {
                 match *self {
-                    CodeExtent::Misc(_) => Some(blk.span),
+                    CodeExtent::Misc(_) |
+                    CodeExtent::DestructionScope(_) => Some(blk.span),
 
                     CodeExtent::Remainder(r) => {
                         assert_eq!(r.block, blk.id);
@@ -455,7 +531,7 @@ impl RegionMaps {
                 }
 
                 (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)) => {
@@ -567,7 +643,18 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
     let prev_cx = visitor.cx;
 
     let blk_scope = CodeExtent::Misc(blk.id);
-    record_superlifetime(visitor, blk_scope, blk.span);
+    // 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);
+    }
 
     // We treat the tail expression in the block (if any) somewhat
     // 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
     // regions referenced by the destructors need to survive.
     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;
     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);
 
     let expr_scope = CodeExtent::Misc(expr.id);
-    record_superlifetime(visitor, expr_scope, expr.span);
+    // 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);
+    }
 
     let prev_cx = visitor.cx;
     visitor.cx.parent = InnermostEnclosingExpr::Some(expr.id);
 
     {
         let region_maps = &mut visitor.region_maps;
-        let terminating = |id| {
-            let scope = CodeExtent::from_node_id(id);
+        let terminating = |e: &P<ast::Expr>| {
+            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)
         };
         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) => {
                 // For shortcircuiting operators, mark the RHS as a terminating
                 // scope since it only executes conditionally.
-                terminating(r.id);
+                terminating(r);
             }
 
             ast::ExprIf(_, ref then, Some(ref otherwise)) => {
-                terminating(then.id);
-                terminating(otherwise.id);
+                terminating_block(then);
+                terminating(otherwise);
             }
 
             ast::ExprIf(ref expr, ref then, None) => {
-                terminating(expr.id);
-                terminating(then.id);
+                terminating(expr);
+                terminating_block(then);
             }
 
             ast::ExprLoop(ref body, _) => {
-                terminating(body.id);
+                terminating_block(body);
             }
 
             ast::ExprWhile(ref expr, ref body, _) => {
-                terminating(expr.id);
-                terminating(body.id);
+                terminating(expr);
+                terminating_block(body);
             }
 
             ast::ExprMatch(..) => {
@@ -1021,6 +1125,9 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
 
     let body_scope = CodeExtent::from_node_id(body.id);
     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;
 
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 6f38dc81064..e91d7d8c52c 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -41,7 +41,7 @@ pub enum DefRegion {
                         /* lifetime decl */ ast::NodeId),
     DefLateBoundRegion(ty::DebruijnIndex,
                        /* lifetime decl */ ast::NodeId),
-    DefFreeRegion(/* block scope */ region::CodeExtent,
+    DefFreeRegion(/* block scope */ region::DestructionScopeData,
                   /* lifetime decl */ ast::NodeId),
 }
 
@@ -81,7 +81,7 @@ enum ScopeChain<'a> {
     LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>),
     /// lifetimes introduced by items within a code block are scoped
     /// to that block.
-    BlockScope(region::CodeExtent, Scope<'a>),
+    BlockScope(region::DestructionScopeData, Scope<'a>),
     RootScope
 }
 
@@ -191,7 +191,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
     }
 
     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));
     }
 
@@ -393,9 +394,13 @@ impl<'a> LifetimeContext<'a> {
     }
 
     fn resolve_free_lifetime_ref(&mut self,
-                                 scope_data: region::CodeExtent,
+                                 scope_data: region::DestructionScopeData,
                                  lifetime_ref: &ast::Lifetime,
                                  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,
         // until we encounter a scope that contains the named lifetime
         // or we run out of scopes.
@@ -403,6 +408,9 @@ impl<'a> LifetimeContext<'a> {
         let mut scope = scope;
         let mut search_result = None;
         loop {
+            debug!("resolve_free_lifetime_ref \
+                    scope_data: {:?} scope: {:?} search_result: {:?}",
+                   scope_data, scope, search_result);
             match *scope {
                 BlockScope(blk_scope_data, s) => {
                     scope_data = blk_scope_data;
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 97e6517a615..8e94991f656 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -72,6 +72,8 @@ use std::cell::{Cell, RefCell};
 use std::cmp;
 use std::fmt;
 use std::hash::{Hash, Writer, SipHasher, Hasher};
+#[cfg(stage0)]
+use std::marker;
 use std::mem;
 use std::ops;
 use std::rc::Rc;
@@ -931,6 +933,26 @@ pub struct TyS<'tcx> {
 
     // the maximal depth of any bound regions appearing in this type.
     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 {
@@ -939,11 +961,20 @@ impl fmt::Debug for TypeFlags {
     }
 }
 
+#[cfg(stage0)]
 impl<'tcx> PartialEq for TyS<'tcx> {
-    fn eq(&self, other: &TyS<'tcx>) -> bool {
+    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> {
+    fn eq(&self, other: &TyS<'tcx>) -> bool {
+        // (self as *const _) == (other as *const _)
+        (self as *const TyS<'tcx>) == (other as *const TyS<'tcx>)
+    }
+}
 impl<'tcx> Eq for TyS<'tcx> {}
 
 impl<'tcx, S: Writer + Hasher> Hash<S> for TyS<'tcx> {
@@ -1174,7 +1205,9 @@ pub enum Region {
     /// region parameters.
     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),
 
     /// 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
 /// at least as big as the scope `fr.scope`".
 pub struct FreeRegion {
-    pub scope: region::CodeExtent,
+    pub scope: region::DestructionScopeData,
     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 ty = type_arena.alloc(TyS {
-        sty: st,
-        flags: flags.flags,
-        region_depth: flags.depth,
-    });
+    let ty = match () {
+        #[cfg(stage0)]
+        () => type_arena.alloc(TyS { sty: st,
+                                     flags: flags.flags,
+                                     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: {:?}",
            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::ReFree(ty::FreeRegion { scope: region::CodeExtent::from_node_id(free_id),
-                                bound_region: ty::BrNamed(def.def_id,
-                                                          def.name) })
+    let ret =
+        ty::ReFree(ty::FreeRegion { scope: outlives_extent,
+                                    bound_region: ty::BrNamed(def.def_id,
+                                                              def.name) });
+    debug!("free_region_from_def returns {:?}", ret);
+    ret
 }
 
 // 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();
     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
     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 {
         types: types,
@@ -6262,11 +6307,11 @@ pub fn construct_free_substs<'a,'tcx>(
     };
 
     fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,
-                          free_id: ast::NodeId,
+                          all_outlive_extent: region::DestructionScopeData,
                           region_params: &[RegionParameterDef])
     {
         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_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.
     //
 
     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();
 
     //
@@ -6335,7 +6380,7 @@ pub fn construct_parameter_environment<'a,'tcx>(
     let unnormalized_env = ty::ParameterEnvironment {
         tcx: tcx,
         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,
         selection_cache: traits::SelectionCache::new(),
     };
@@ -6603,14 +6648,14 @@ impl<'tcx> AutoDerefRef<'tcx> {
 /// `scope_id`.
 pub fn liberate_late_bound_regions<'tcx, T>(
     tcx: &ty::ctxt<'tcx>,
-    scope: region::CodeExtent,
+    all_outlive_scope: region::DestructionScopeData,
     value: &Binder<T>)
     -> T
     where T : TypeFoldable<'tcx> + Repr<'tcx>
 {
     replace_late_bound_regions(
         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>(
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index c72b7fdf7ad..397d27db3b9 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -113,6 +113,10 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
         };
         let scope_decorated_tag = match scope {
             region::CodeExtent::Misc(_) => tag,
+            region::CodeExtent::DestructionScope(_) => {
+                new_string = format!("destruction scope surrounding {}", tag);
+                new_string.as_slice()
+            }
             region::CodeExtent::Remainder(r) => {
                 new_string = format!("block suffix following statement {}",
                                      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)) => {
               let (msg, opt_span) = explain_span(cx, "block", blk.span);
               (format!("{} {}", prefix, msg), opt_span)
@@ -921,7 +925,7 @@ impl<'tcx> UserString<'tcx> for ty::Region {
 impl<'tcx> Repr<'tcx> for ty::FreeRegion {
     fn repr(&self, tcx: &ctxt) -> String {
         format!("ReFree({}, {})",
-                self.scope.node_id(),
+                self.scope.repr(tcx),
                 self.bound_region.repr(tcx))
     }
 }
@@ -931,12 +935,23 @@ impl<'tcx> Repr<'tcx> for region::CodeExtent {
         match *self {
             region::CodeExtent::Misc(node_id) =>
                 format!("Misc({})", node_id),
+            region::CodeExtent::DestructionScope(node_id) =>
+                format!("DestructionScope({})", node_id),
             region::CodeExtent::Remainder(rem) =>
                 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 {
     fn repr(&self, tcx: &ctxt) -> String {
         // Unfortunately, there seems to be no way to attempt to print
diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
index b1cc3a65120..4e308c5809f 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs
@@ -286,7 +286,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
                 let loan_scope = match loan_region {
                     ty::ReScope(scope) => scope,
 
-                    ty::ReFree(ref fr) => fr.scope,
+                    ty::ReFree(ref fr) => fr.scope.to_code_extent(),
 
                     ty::ReStatic => {
                         // If we get here, an error must have been
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 9df90258462..7105a6cc488 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -15,7 +15,7 @@ use diagnostic::Emitter;
 use driver;
 use rustc_resolve as resolve;
 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::stability;
 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 {
-        ty::ReFree(ty::FreeRegion { scope: CodeExtent::from_node_id(nid),
+        ty::ReFree(ty::FreeRegion { scope: DestructionScopeData::new(nid),
                                     bound_region: ty::BrAnon(id)})
     }
 
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index a261599a706..0913a245c1b 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2196,7 +2196,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // Search for external modules.
         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 =
                     Rc::new(Resolver::create_name_bindings_from_module(module));
                 debug!("lower name bindings succeeded");
@@ -2481,7 +2483,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // Finally, search through external children.
         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 =
                     Rc::new(Resolver::create_name_bindings_from_module(module));
                 return Success((Target::new(module_,
diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs
index bebba151a0d..f7e37b6f633 100644
--- a/src/librustc_trans/trans/cleanup.rs
+++ b/src/librustc_trans/trans/cleanup.rs
@@ -130,12 +130,17 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
         // this new AST scope had better be its immediate child.
         let top_scope = self.top_ast_scope();
         if top_scope.is_some() {
-            assert_eq!(self.ccx
-                           .tcx()
-                           .region_maps
-                           .opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id))
-                           .map(|s|s.node_id()),
-                       top_scope);
+            assert!((self.ccx
+                     .tcx()
+                     .region_maps
+                     .opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id))
+                     .map(|s|s.node_id()) == 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),
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index b2a676e878e..0b7c5b04aaa 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -13,7 +13,7 @@
 use super::{check_fn, Expectation, FnCtxt};
 
 use astconv;
-use middle::region::CodeExtent;
+use middle::region;
 use middle::subst;
 use middle::ty::{self, ToPolyTraitRef, Ty};
 use rscope::RegionScope;
@@ -78,7 +78,9 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
     fcx.write_ty(expr.id, closure_type);
 
     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,
              ast::Unsafety::Normal,
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
new file mode 100644
index 00000000000..1be6bf05c99
--- /dev/null
+++ b/src/librustc_typeck/check/dropck.rs
@@ -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.
+        }
+    }
+}
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index ba49ae637b3..cfc04a9a92f 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -525,7 +525,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                                                             trait_def_id);
 
         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; }
             Some(impls) => impls,
         };
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 9c0d6f7dae3..d90ed7eda59 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -90,7 +90,7 @@ use middle::infer;
 use middle::mem_categorization as mc;
 use middle::mem_categorization::McResult;
 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::traits;
 use middle::ty::{FnSig, VariantInfo, TypeScheme};
@@ -127,6 +127,7 @@ use syntax::ptr::P;
 use syntax::visit::{self, Visitor};
 
 mod assoc;
+pub mod dropck;
 pub mod _match;
 pub mod vtable;
 pub mod writeback;
@@ -495,7 +496,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
             let fn_sig =
                 fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs);
             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 =
                 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,
                                                         ast_t.span,
-                                                        CodeExtent::from_node_id(self.body_id),
+                                                        self.body_id,
                                                         None);
         bounds_checker.check_ty(t);
 
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 9df0403794d..80f6e3800f7 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -83,6 +83,7 @@
 //! contents.
 
 use astconv::AstConv;
+use check::dropck;
 use check::FnCtxt;
 use check::regionmanip;
 use check::vtable;
@@ -171,6 +172,7 @@ pub struct Rcx<'a, 'tcx: 'a> {
 
     // id of AST node being analyzed (the subject of the analysis).
     subject: SubjectNode,
+
 }
 
 /// 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,
               repeating_scope: initial_repeating_scope,
               subject: subject,
-              region_bound_pairs: Vec::new() }
+              region_bound_pairs: Vec::new()
+        }
     }
 
     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(
             rcx, infer::BindingTypeIsNotValidAtDecl(span),
             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 {
@@ -995,6 +1036,33 @@ pub fn mk_subregion_due_to_dereference(rcx: &mut Rcx,
                     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
 /// 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`.
-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>,
                                ty: Ty<'tcx>,
                                region: ty::Region)
diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs
index 1079e87a48b..923614f9e8a 100644
--- a/src/librustc_typeck/check/wf.rs
+++ b/src/librustc_typeck/check/wf.rs
@@ -145,9 +145,11 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
             let variants = lookup_fields(fcx);
             let mut bounds_checker = BoundsChecker::new(fcx,
                                                         item.span,
-                                                        region::CodeExtent::from_node_id(item.id),
+                                                        item.id,
                                                         Some(&mut this.cache));
-            for variant in &variants {
+            debug!("check_type_defn at bounds_checker.scope: {:?}", bounds_checker.scope);
+
+             for variant in &variants {
                 for field in &variant.fields {
                     // Regions are checked below.
                     bounds_checker.check_traits_in_ty(field.ty);
@@ -180,8 +182,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
         self.with_fcx(item, |this, fcx| {
             let mut bounds_checker = BoundsChecker::new(fcx,
                                                         item.span,
-                                                        region::CodeExtent::from_node_id(item.id),
+                                                        item.id,
                                                         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 item_ty = fcx.instantiate_type_scheme(item.span,
@@ -196,12 +199,11 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                   item: &ast::Item)
     {
         self.with_fcx(item, |this, fcx| {
-            let item_scope = region::CodeExtent::from_node_id(item.id);
-
             let mut bounds_checker = BoundsChecker::new(fcx,
                                                         item.span,
-                                                        item_scope,
+                                                        item.id,
                                                         Some(&mut this.cache));
+            debug!("check_impl at bounds_checker.scope: {:?}", bounds_checker.scope);
 
             // Find the impl self type as seen from the "inside" --
             // 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> {
     fcx: &'cx FnCtxt<'cx,'tcx>,
     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,
     cache: Option<&'cx mut HashSet<Ty<'tcx>>>,
 }
@@ -391,7 +398,7 @@ pub struct BoundsChecker<'cx,'tcx:'cx> {
 impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
     pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>,
                span: Span,
-               scope: region::CodeExtent,
+               scope: ast::NodeId,
                cache: Option<&'cx mut HashSet<Ty<'tcx>>>)
                -> BoundsChecker<'cx,'tcx> {
         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>
     {
         self.binding_count += 1;
-        let value = liberate_late_bound_regions(self.fcx.tcx(), self.scope, binder);
-        debug!("BoundsChecker::fold_binder: late-bound regions replaced: {}",
-               value.repr(self.tcx()));
+        let value = liberate_late_bound_regions(
+            self.fcx.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);
         self.binding_count -= 1;
         ty::Binder(value)
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index ce26658cf4b..55fa47760bb 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1564,7 +1564,7 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
             _ => 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
         // 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>(
         tcx: &ty::ctxt<'tcx>,
-        scope: region::CodeExtent,
+        scope: region::DestructionScopeData,
         value: &T)
         -> T
         where T : TypeFoldable<'tcx> + Repr<'tcx>
diff --git a/src/libstd/old_io/mod.rs b/src/libstd/old_io/mod.rs
index c3e4e7fc80d..219da8f44eb 100644
--- a/src/libstd/old_io/mod.rs
+++ b/src/libstd/old_io/mod.rs
@@ -50,7 +50,8 @@
 //!     ```rust
 //!     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());
 //!     }
 //!     ```
diff --git a/src/libstd/old_io/stdio.rs b/src/libstd/old_io/stdio.rs
index 70400619bea..4b3811bbe38 100644
--- a/src/libstd/old_io/stdio.rs
+++ b/src/libstd/old_io/stdio.rs
@@ -143,7 +143,8 @@ impl StdinReader {
     /// ```rust
     /// 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());
     /// }
     /// ```
diff --git a/src/test/auxiliary/issue-2631-a.rs b/src/test/auxiliary/issue-2631-a.rs
index e340331dbfd..dd1ad413a3d 100644
--- a/src/test/auxiliary/issue-2631-a.rs
+++ b/src/test/auxiliary/issue-2631-a.rs
@@ -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
 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();
 }
diff --git a/src/test/bench/shootout-k-nucleotide.rs b/src/test/bench/shootout-k-nucleotide.rs
index 474e5464293..74d21687972 100644
--- a/src/test/bench/shootout-k-nucleotide.rs
+++ b/src/test/bench/shootout-k-nucleotide.rs
@@ -295,7 +295,9 @@ fn main() {
         let fd = std::old_io::File::open(&Path::new("shootout-k-nucleotide.data"));
         get_sequence(&mut std::old_io::BufferedReader::new(fd), ">THREE")
     } 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);
 
diff --git a/src/test/bench/sudoku.rs b/src/test/bench/sudoku.rs
index 4a248384e10..c5a64db95e6 100644
--- a/src/test/bench/sudoku.rs
+++ b/src/test/bench/sudoku.rs
@@ -274,7 +274,9 @@ fn main() {
     let mut sudoku = if use_default {
         Sudoku::from_vec(&DEFAULT_SUDOKU)
     } 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.write(&mut old_io::stdout());
diff --git a/src/test/compile-fail/destructor-restrictions.rs b/src/test/compile-fail/destructor-restrictions.rs
new file mode 100644
index 00000000000..0836cd1695d
--- /dev/null
+++ b/src/test/compile-fail/destructor-restrictions.rs
@@ -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);
+}
diff --git a/src/test/compile-fail/dropck_arr_cycle_checked.rs b/src/test/compile-fail/dropck_arr_cycle_checked.rs
new file mode 100644
index 00000000000..3aa2fae2826
--- /dev/null
+++ b/src/test/compile-fail/dropck_arr_cycle_checked.rs
@@ -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();
+}
diff --git a/src/test/compile-fail/dropck_direct_cycle_with_drop.rs b/src/test/compile-fail/dropck_direct_cycle_with_drop.rs
new file mode 100644
index 00000000000..b9df71322ad
--- /dev/null
+++ b/src/test/compile-fail/dropck_direct_cycle_with_drop.rs
@@ -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();
+}
diff --git a/src/test/compile-fail/dropck_tarena_cycle_checked.rs b/src/test/compile-fail/dropck_tarena_cycle_checked.rs
new file mode 100644
index 00000000000..74e3c724b67
--- /dev/null
+++ b/src/test/compile-fail/dropck_tarena_cycle_checked.rs
@@ -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
+}
diff --git a/src/test/compile-fail/dropck_tarena_unsound_drop.rs b/src/test/compile-fail/dropck_tarena_unsound_drop.rs
new file mode 100644
index 00000000000..64d77e97fa7
--- /dev/null
+++ b/src/test/compile-fail/dropck_tarena_unsound_drop.rs
@@ -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
+}
diff --git a/src/test/compile-fail/dropck_vec_cycle_checked.rs b/src/test/compile-fail/dropck_vec_cycle_checked.rs
new file mode 100644
index 00000000000..3f69c7d1a9c
--- /dev/null
+++ b/src/test/compile-fail/dropck_vec_cycle_checked.rs
@@ -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();
+}
diff --git a/src/test/compile-fail/vec-must-not-hide-type-from-dropck.rs b/src/test/compile-fail/vec-must-not-hide-type-from-dropck.rs
new file mode 100644
index 00000000000..6aaf51278af
--- /dev/null
+++ b/src/test/compile-fail/vec-must-not-hide-type-from-dropck.rs
@@ -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();
+}
diff --git a/src/test/compile-fail/vec_refs_data_with_early_death.rs b/src/test/compile-fail/vec_refs_data_with_early_death.rs
new file mode 100644
index 00000000000..a191b3e56c4
--- /dev/null
+++ b/src/test/compile-fail/vec_refs_data_with_early_death.rs
@@ -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]);
+}
diff --git a/src/test/run-pass/arr_cycle.rs b/src/test/run-pass/arr_cycle.rs
new file mode 100644
index 00000000000..80434f36b42
--- /dev/null
+++ b/src/test/run-pass/arr_cycle.rs
@@ -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();
+}
diff --git a/src/test/run-pass/dropck_tarena_sound_drop.rs b/src/test/run-pass/dropck_tarena_sound_drop.rs
new file mode 100644
index 00000000000..ad71f725864
--- /dev/null
+++ b/src/test/run-pass/dropck_tarena_sound_drop.rs
@@ -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);
+}
diff --git a/src/test/run-pass/issue-13304.rs b/src/test/run-pass/issue-13304.rs
index f979235da71..4dc824d9068 100644
--- a/src/test/run-pass/issue-13304.rs
+++ b/src/test/run-pass/issue-13304.rs
@@ -37,7 +37,8 @@ fn parent() {
 }
 
 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());
     }
 }
diff --git a/src/test/run-pass/issue-14456.rs b/src/test/run-pass/issue-14456.rs
index 5f44eb7dcd2..1c8066bc3c9 100644
--- a/src/test/run-pass/issue-14456.rs
+++ b/src/test/run-pass/issue-14456.rs
@@ -27,7 +27,8 @@ fn main() {
 fn child() {
     old_io::stdout().write_line("foo").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() {
diff --git a/src/test/run-pass/multidispatch-conditional-impl-not-considered.rs b/src/test/run-pass/multidispatch-conditional-impl-not-considered.rs
index 5db5a6267bf..49ecef9c735 100644
--- a/src/test/run-pass/multidispatch-conditional-impl-not-considered.rs
+++ b/src/test/run-pass/multidispatch-conditional-impl-not-considered.rs
@@ -29,5 +29,5 @@ impl Bar {
 
 fn main() {
     let b = RefCell::new(Bar);
-    b.borrow().foo()
+    b.borrow().foo();
 }
diff --git a/src/test/run-pass/nondrop-cycle.rs b/src/test/run-pass/nondrop-cycle.rs
new file mode 100644
index 00000000000..bbce9a8f4a6
--- /dev/null
+++ b/src/test/run-pass/nondrop-cycle.rs
@@ -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();
+}
diff --git a/src/test/run-pass/overloaded-autoderef.rs b/src/test/run-pass/overloaded-autoderef.rs
index fcf0feb6e30..faa79f71d9e 100644
--- a/src/test/run-pass/overloaded-autoderef.rs
+++ b/src/test/run-pass/overloaded-autoderef.rs
@@ -22,8 +22,9 @@ struct Point {
 }
 
 pub fn main() {
+    let box_5 = box 5u;
     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});
     assert_eq!(point.x, 2);
     assert_eq!(point.y, 4);
diff --git a/src/test/run-pass/regions-refcell.rs b/src/test/run-pass/regions-refcell.rs
new file mode 100644
index 00000000000..019db2a977e
--- /dev/null
+++ b/src/test/run-pass/regions-refcell.rs
@@ -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);
+}
diff --git a/src/test/run-pass/regions-trait-object-1.rs b/src/test/run-pass/regions-trait-object-1.rs
new file mode 100644
index 00000000000..eb3bec77326
--- /dev/null
+++ b/src/test/run-pass/regions-trait-object-1.rs
@@ -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);
+}
diff --git a/src/test/run-pass/trait-object-with-lifetime-bound.rs b/src/test/run-pass/trait-object-with-lifetime-bound.rs
new file mode 100644
index 00000000000..4e481910aa9
--- /dev/null
+++ b/src/test/run-pass/trait-object-with-lifetime-bound.rs
@@ -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);
+}
diff --git a/src/test/run-pass/vec_cycle.rs b/src/test/run-pass/vec_cycle.rs
new file mode 100644
index 00000000000..65522bd95df
--- /dev/null
+++ b/src/test/run-pass/vec_cycle.rs
@@ -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();
+}
diff --git a/src/test/run-pass/vec_cycle_wrapped.rs b/src/test/run-pass/vec_cycle_wrapped.rs
new file mode 100644
index 00000000000..f179df90b34
--- /dev/null
+++ b/src/test/run-pass/vec_cycle_wrapped.rs
@@ -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();
+}