diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index f56a27b9ae04a..3a06c6f3b9657 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -408,6 +408,7 @@ impl<'a> LoweringContext<'a> {
             bounds: self.lower_bounds(&tp.bounds),
             default: tp.default.as_ref().map(|x| self.lower_ty(x)),
             span: tp.span,
+            pure_wrt_drop: tp.attrs.iter().any(|attr| attr.check_name("may_dangle")),
         }
     }
 
@@ -427,6 +428,7 @@ impl<'a> LoweringContext<'a> {
         hir::LifetimeDef {
             lifetime: self.lower_lifetime(&l.lifetime),
             bounds: self.lower_lifetimes(&l.bounds),
+            pure_wrt_drop: l.attrs.iter().any(|attr| attr.check_name("may_dangle")),
         }
     }
 
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index f64b0e9c7342c..6e81c3e700ed2 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -95,6 +95,7 @@ impl fmt::Debug for Lifetime {
 pub struct LifetimeDef {
     pub lifetime: Lifetime,
     pub bounds: HirVec<Lifetime>,
+    pub pure_wrt_drop: bool,
 }
 
 /// A "Path" is essentially Rust's notion of a name; for instance:
@@ -290,6 +291,7 @@ pub struct TyParam {
     pub bounds: TyParamBounds,
     pub default: Option<P<Ty>>,
     pub span: Span,
+    pub pure_wrt_drop: bool,
 }
 
 /// Represents lifetimes and type parameters attached to a declaration
@@ -328,6 +330,36 @@ impl Generics {
     }
 }
 
+pub enum UnsafeGeneric {
+    Region(LifetimeDef, &'static str),
+    Type(TyParam, &'static str),
+}
+
+impl UnsafeGeneric {
+    pub fn attr_name(&self) -> &'static str {
+        match *self {
+            UnsafeGeneric::Region(_, s) => s,
+            UnsafeGeneric::Type(_, s) => s,
+        }
+    }
+}
+
+impl Generics {
+    pub fn carries_unsafe_attr(&self) -> Option<UnsafeGeneric> {
+        for r in &self.lifetimes {
+            if r.pure_wrt_drop {
+                return Some(UnsafeGeneric::Region(r.clone(), "may_dangle"));
+            }
+        }
+        for t in &self.ty_params {
+            if t.pure_wrt_drop {
+                return Some(UnsafeGeneric::Type(t.clone(), "may_dangle"));
+            }
+        }
+        return None;
+    }
+}
+
 /// A `where` clause in a definition
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct WhereClause {
diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs
index 3f216d6916851..ddb43702ef36c 100644
--- a/src/librustc/infer/error_reporting.rs
+++ b/src/librustc/infer/error_reporting.rs
@@ -1226,16 +1226,17 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
                          lifetime: hir::Lifetime,
                          region_names: &HashSet<ast::Name>)
                          -> hir::HirVec<hir::TyParam> {
-        ty_params.iter().map(|ty_param| {
-            let bounds = self.rebuild_ty_param_bounds(ty_param.bounds.clone(),
+        ty_params.into_iter().map(|ty_param| {
+            let bounds = self.rebuild_ty_param_bounds(ty_param.bounds,
                                                       lifetime,
                                                       region_names);
             hir::TyParam {
                 name: ty_param.name,
                 id: ty_param.id,
                 bounds: bounds,
-                default: ty_param.default.clone(),
+                default: ty_param.default,
                 span: ty_param.span,
+                pure_wrt_drop: ty_param.pure_wrt_drop,
             }
         }).collect()
     }
@@ -1294,8 +1295,11 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
                         -> hir::Generics {
         let mut lifetimes = Vec::new();
         for lt in add {
-            lifetimes.push(hir::LifetimeDef { lifetime: *lt,
-                                              bounds: hir::HirVec::new() });
+            lifetimes.push(hir::LifetimeDef {
+                lifetime: *lt,
+                bounds: hir::HirVec::new(),
+                pure_wrt_drop: false,
+            });
         }
         for lt in &generics.lifetimes {
             if keep.contains(&lt.lifetime.name) ||
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 8e330ee8e824c..7a22ef3c81edc 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -680,6 +680,11 @@ pub struct TypeParameterDef<'tcx> {
     pub default_def_id: DefId, // for use in error reporing about defaults
     pub default: Option<Ty<'tcx>>,
     pub object_lifetime_default: ObjectLifetimeDefault<'tcx>,
+
+    /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
+    /// on generic parameter `T`, asserts data behind the parameter
+    /// `T` won't be accessed during the parent type's `Drop` impl.
+    pub pure_wrt_drop: bool,
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable)]
@@ -688,6 +693,11 @@ pub struct RegionParameterDef<'tcx> {
     pub def_id: DefId,
     pub index: u32,
     pub bounds: Vec<&'tcx ty::Region>,
+
+    /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
+    /// on generic parameter `'a`, asserts data of lifetime `'a`
+    /// won't be accessed during the parent type's `Drop` impl.
+    pub pure_wrt_drop: bool,
 }
 
 impl<'tcx> RegionParameterDef<'tcx> {
@@ -732,6 +742,14 @@ impl<'tcx> Generics<'tcx> {
     pub fn count(&self) -> usize {
         self.parent_count() + self.own_count()
     }
+
+    pub fn region_param(&self, param: &EarlyBoundRegion) -> &RegionParameterDef<'tcx> {
+        &self.regions[param.index as usize - self.has_self as usize]
+    }
+
+    pub fn type_param(&self, param: &ParamTy) -> &TypeParameterDef<'tcx> {
+        &self.types[param.idx as usize - self.has_self as usize - self.regions.len()]
+    }
 }
 
 /// Bounds on generics.
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index abd5cb51f39ba..40eb6d71280a6 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -716,6 +716,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
             default: self.default.fold_with(folder),
             default_def_id: self.default_def_id,
             object_lifetime_default: self.object_lifetime_default.fold_with(folder),
+            pure_wrt_drop: self.pure_wrt_drop,
         }
     }
 
@@ -754,6 +755,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef<'tcx> {
             def_id: self.def_id,
             index: self.index,
             bounds: self.bounds.fold_with(folder),
+            pure_wrt_drop: self.pure_wrt_drop,
         }
     }
 
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index cc958fb3b2343..5e2b49bac1b2f 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -71,7 +71,8 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
     ccx: &CrateCtxt<'a, 'tcx>,
     drop_impl_did: DefId,
     drop_impl_ty: Ty<'tcx>,
-    self_type_did: DefId) -> Result<(), ()>
+    self_type_did: DefId)
+    -> Result<(), ()>
 {
     let tcx = ccx.tcx;
     let drop_impl_node_id = tcx.map.as_local_node_id(drop_impl_did).unwrap();
@@ -123,7 +124,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
     drop_impl_did: DefId,
     dtor_predicates: &ty::GenericPredicates<'tcx>,
     self_type_did: DefId,
-    self_to_impl_substs: &Substs<'tcx>) -> Result<(), ()> {
+    self_to_impl_substs: &Substs<'tcx>)
+    -> Result<(), ()>
+{
 
     // Here is an example, analogous to that from
     // `compare_impl_method`.
@@ -350,7 +353,8 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
     cx: &mut DropckContext<'a, 'b, 'gcx, 'tcx>,
     context: TypeContext,
     ty: Ty<'tcx>,
-    depth: usize) -> Result<(), Error<'tcx>>
+    depth: usize)
+    -> Result<(), Error<'tcx>>
 {
     let tcx = cx.rcx.tcx;
     // Issue #22443: Watch out for overflow. While we are careful to
@@ -402,16 +406,27 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
     // unbounded type parameter `T`, we must resume the recursive
     // analysis on `T` (since it would be ignored by
     // type_must_outlive).
-    if has_dtor_of_interest(tcx, ty) {
-        debug!("iterate_over_potentially_unsafe_regions_in_type \
-                {}ty: {} - is a dtorck type!",
-               (0..depth).map(|_| ' ').collect::<String>(),
-               ty);
-
-        cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
-                                 ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
-
-        return Ok(());
+    let dropck_kind = has_dtor_of_interest(tcx, ty);
+    debug!("iterate_over_potentially_unsafe_regions_in_type \
+            ty: {:?} dropck_kind: {:?}", ty, dropck_kind);
+    match dropck_kind {
+        DropckKind::NoBorrowedDataAccessedInMyDtor => {
+            // The maximally blind attribute.
+        }
+        DropckKind::BorrowedDataMustStrictlyOutliveSelf => {
+            cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
+                                     ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
+            return Ok(());
+        }
+        DropckKind::RevisedSelf(revised_ty) => {
+            cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
+                                     revised_ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
+            // Do not return early from this case; we want
+            // to recursively process the internal structure of Self
+            // (because even though the Drop for Self has been asserted
+            //  safe, the types instantiated for the generics of Self
+            //  may themselves carry dropck constraints.)
+        }
     }
 
     debug!("iterate_over_potentially_unsafe_regions_in_type \
@@ -492,16 +507,140 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum DropckKind<'tcx> {
+    /// The "safe" kind; i.e. conservatively assume any borrow
+    /// accessed by dtor, and therefore such data must strictly
+    /// outlive self.
+    ///
+    /// Equivalent to RevisedTy with no change to the self type.
+    BorrowedDataMustStrictlyOutliveSelf,
+
+    /// The nearly completely-unsafe kind.
+    ///
+    /// Equivalent to RevisedSelf with *all* parameters remapped to ()
+    /// (maybe...?)
+    NoBorrowedDataAccessedInMyDtor,
+
+    /// Assume all borrowed data access by dtor occurs as if Self has the
+    /// type carried by this variant. In practice this means that some
+    /// of the type parameters are remapped to `()` (and some lifetime
+    /// parameters remapped to `'static`), because the developer has asserted
+    /// that the destructor will not access their contents.
+    RevisedSelf(Ty<'tcx>),
+}
+
+/// Returns the classification of what kind of check should be applied
+/// to `ty`, which may include a revised type where some of the type
+/// parameters are re-mapped to `()` to reflect the destructor's
+/// "purity" with respect to their actual contents.
 fn has_dtor_of_interest<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                                        ty: Ty<'tcx>) -> bool {
+                                        ty: Ty<'tcx>)
+                                        -> DropckKind<'tcx> {
     match ty.sty {
-        ty::TyAdt(def, _) => {
-            def.is_dtorck(tcx)
+        ty::TyAdt(adt_def, substs) => {
+            if !adt_def.is_dtorck(tcx) {
+                return DropckKind::NoBorrowedDataAccessedInMyDtor;
+            }
+
+            // Find the `impl<..> Drop for _` to inspect any
+            // attributes attached to the impl's generics.
+            let dtor_method = adt_def.destructor()
+                .expect("dtorck type without destructor impossible");
+            let method = tcx.impl_or_trait_item(dtor_method);
+            let impl_id: DefId = method.container().id();
+            let revised_ty = revise_self_ty(tcx, adt_def, impl_id, substs);
+            return DropckKind::RevisedSelf(revised_ty);
         }
         ty::TyTrait(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
             debug!("ty: {:?} isn't known, and therefore is a dropck type", ty);
-            true
+            return DropckKind::BorrowedDataMustStrictlyOutliveSelf;
         },
-        _ => false
+        _ => {
+            return DropckKind::NoBorrowedDataAccessedInMyDtor;
+        }
     }
 }
+
+// Constructs new Ty just like the type defined by `adt_def` coupled
+// with `substs`, except each type and lifetime parameter marked as
+// `#[may_dangle]` in the Drop impl (identified by `impl_id`) is
+// respectively mapped to `()` or `'static`.
+//
+// For example: If the `adt_def` maps to:
+//
+//   enum Foo<'a, X, Y> { ... }
+//
+// and the `impl_id` maps to:
+//
+//   impl<#[may_dangle] 'a, X, #[may_dangle] Y> Drop for Foo<'a, X, Y> { ... }
+//
+// then revises input: `Foo<'r,i64,&'r i64>` to: `Foo<'static,i64,()>`
+fn revise_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                  adt_def: ty::AdtDef<'tcx>,
+                                  impl_id: DefId,
+                                  substs: &Substs<'tcx>)
+                                  -> Ty<'tcx> {
+    // Get generics for `impl Drop` to query for `#[may_dangle]` attr.
+    let impl_bindings = tcx.lookup_generics(impl_id);
+
+    // Get Substs attached to Self on `impl Drop`; process in parallel
+    // with `substs`, replacing dangling entries as appropriate.
+    let self_substs = {
+        let impl_self_ty: Ty<'tcx> = tcx.lookup_item_type(impl_id).ty;
+        if let ty::TyAdt(self_adt_def, self_substs) = impl_self_ty.sty {
+            assert_eq!(adt_def, self_adt_def);
+            self_substs
+        } else {
+            bug!("Self in `impl Drop for _` must be an Adt.");
+        }
+    };
+
+    // Walk `substs` + `self_substs`, build new substs appropriate for
+    // `adt_def`; each non-dangling param reuses entry from `substs`.
+    //
+    // Note: The manner we map from a right-hand side (i.e. Region or
+    // Ty) for a given `def` to generic parameter associated with that
+    // right-hand side is tightly coupled to `Drop` impl constraints.
+    //
+    // E.g. we know such a Ty must be `TyParam`, because a destructor
+    // for `struct Foo<X>` is defined via `impl<Y> Drop for Foo<Y>`,
+    // and never by (for example) `impl<Z> Drop for Foo<Vec<Z>>`.
+    let substs = Substs::for_item(
+        tcx,
+        adt_def.did,
+        |def, _| {
+            let r_orig = substs.region_for_def(def);
+            let impl_self_orig = self_substs.region_for_def(def);
+            let r = if let ty::Region::ReEarlyBound(ref ebr) = *impl_self_orig {
+                if impl_bindings.region_param(ebr).pure_wrt_drop {
+                    tcx.mk_region(ty::ReStatic)
+                } else {
+                    r_orig
+                }
+            } else {
+                bug!("substs for an impl must map regions to ReEarlyBound");
+            };
+            debug!("has_dtor_of_interest mapping def {:?} orig {:?} to {:?}",
+                   def, r_orig, r);
+            r
+        },
+        |def, _| {
+            let t_orig = substs.type_for_def(def);
+            let impl_self_orig = self_substs.type_for_def(def);
+            let t = if let ty::TypeVariants::TyParam(ref pt) = impl_self_orig.sty {
+                if impl_bindings.type_param(pt).pure_wrt_drop {
+                    tcx.mk_nil()
+                } else {
+                    t_orig
+                }
+            } else {
+                bug!("substs for an impl must map types to TyParam");
+            };
+            debug!("has_dtor_of_interest mapping def {:?} orig {:?} {:?} to {:?} {:?}",
+                   def, t_orig, t_orig.sty, t, t.sty);
+            t
+        });
+
+    return tcx.mk_adt(adt_def, &substs);
+}
diff --git a/src/librustc_typeck/coherence/unsafety.rs b/src/librustc_typeck/coherence/unsafety.rs
index ff55ce0e5eb55..cca6c88430672 100644
--- a/src/librustc_typeck/coherence/unsafety.rs
+++ b/src/librustc_typeck/coherence/unsafety.rs
@@ -13,7 +13,7 @@
 
 use rustc::ty::TyCtxt;
 use rustc::hir::intravisit;
-use rustc::hir;
+use rustc::hir::{self, Unsafety};
 
 pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
     let mut orphan = UnsafetyChecker { tcx: tcx };
@@ -27,6 +27,7 @@ struct UnsafetyChecker<'cx, 'tcx: 'cx> {
 impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
     fn check_unsafety_coherence(&mut self,
                                 item: &'v hir::Item,
+                                impl_generics: Option<&hir::Generics>,
                                 unsafety: hir::Unsafety,
                                 polarity: hir::ImplPolarity) {
         match self.tcx.impl_trait_ref(self.tcx.map.local_def_id(item.id)) {
@@ -47,15 +48,16 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
 
             Some(trait_ref) => {
                 let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id);
-                match (trait_def.unsafety, unsafety, polarity) {
-                    (hir::Unsafety::Unsafe, hir::Unsafety::Unsafe, hir::ImplPolarity::Negative) => {
+                let unsafe_attr = impl_generics.and_then(|g| g.carries_unsafe_attr());
+                match (trait_def.unsafety, unsafe_attr, unsafety, polarity) {
+                    (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative) => {
                         span_err!(self.tcx.sess,
                                   item.span,
                                   E0198,
                                   "negative implementations are not unsafe");
                     }
 
-                    (hir::Unsafety::Normal, hir::Unsafety::Unsafe, _) => {
+                    (Unsafety::Normal, None, Unsafety::Unsafe, _) => {
                         span_err!(self.tcx.sess,
                                   item.span,
                                   E0199,
@@ -63,7 +65,7 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
                                   trait_ref);
                     }
 
-                    (hir::Unsafety::Unsafe, hir::Unsafety::Normal, hir::ImplPolarity::Positive) => {
+                    (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
                         span_err!(self.tcx.sess,
                                   item.span,
                                   E0200,
@@ -71,9 +73,19 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
                                   trait_ref);
                     }
 
-                    (hir::Unsafety::Unsafe, hir::Unsafety::Normal, hir::ImplPolarity::Negative) |
-                    (hir::Unsafety::Unsafe, hir::Unsafety::Unsafe, hir::ImplPolarity::Positive) |
-                    (hir::Unsafety::Normal, hir::Unsafety::Normal, _) => {
+                    (Unsafety::Normal, Some(g), Unsafety::Normal, hir::ImplPolarity::Positive) =>
+                    {
+                        span_err!(self.tcx.sess,
+                                  item.span,
+                                  E0569,
+                                  "requires an `unsafe impl` declaration due to `#[{}]` attribute",
+                                  g.attr_name());
+                    }
+
+                    (_, _, Unsafety::Normal, hir::ImplPolarity::Negative) |
+                    (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) |
+                    (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) |
+                    (Unsafety::Normal, None, Unsafety::Normal, _) => {
                         // OK
                     }
                 }
@@ -86,10 +98,10 @@ impl<'cx, 'tcx, 'v> intravisit::Visitor<'v> for UnsafetyChecker<'cx, 'tcx> {
     fn visit_item(&mut self, item: &'v hir::Item) {
         match item.node {
             hir::ItemDefaultImpl(unsafety, _) => {
-                self.check_unsafety_coherence(item, unsafety, hir::ImplPolarity::Positive);
+                self.check_unsafety_coherence(item, None, unsafety, hir::ImplPolarity::Positive);
             }
-            hir::ItemImpl(unsafety, polarity, ..) => {
-                self.check_unsafety_coherence(item, unsafety, polarity);
+            hir::ItemImpl(unsafety, polarity, ref generics, ..) => {
+                self.check_unsafety_coherence(item, Some(generics), unsafety, polarity);
             }
             _ => {}
         }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 128db6ef5848a..6e47f4ed8c6be 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1482,6 +1482,7 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                             default_def_id: tcx.map.local_def_id(parent),
                             default: None,
                             object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault,
+                            pure_wrt_drop: false,
                         };
                         tcx.ty_param_defs.borrow_mut().insert(param_id, def.clone());
                         opt_self = Some(def);
@@ -1526,7 +1527,8 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                 def_id: tcx.map.local_def_id(l.lifetime.id),
                 bounds: l.bounds.iter().map(|l| {
                     ast_region_to_region(tcx, l)
-                }).collect()
+                }).collect(),
+                pure_wrt_drop: l.pure_wrt_drop,
             }
         }).collect::<Vec<_>>();
 
@@ -1926,6 +1928,7 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
         default_def_id: ccx.tcx.map.local_def_id(parent),
         default: default,
         object_lifetime_default: object_lifetime_default,
+        pure_wrt_drop: param.pure_wrt_drop,
     };
 
     if def.name == keywords::SelfType.name() {
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 0d6b43b59c6ad..16f915c0011cb 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -2819,6 +2819,26 @@ not a distinct static type. Likewise, it's not legal to attempt to
 behavior for specific enum variants.
 "##,
 
+E0569: r##"
+If an impl has a generic parameter with the `#[may_dangle]` attribute, then
+that impl must be declared as an `unsafe impl. For example:
+
+```compile_fail,E0569
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+struct Foo<X>(X);
+impl<#[may_dangle] X> Drop for Foo<X> {
+    fn drop(&mut self) { }
+}
+```
+
+In this example, we are asserting that the destructor for `Foo` will not
+access any data of type `X`, and require this assertion to be true for
+overall safety in our program. The compiler does not currently attempt to
+verify this assertion; therefore we must tag this `impl` as unsafe.
+"##,
+
 E0318: r##"
 Default impls for a trait must be located in the same crate where the trait was
 defined. For more information see the [opt-in builtin traits RFC](https://github
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9eed1d61244a4..c2eae29c16d5d 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -167,6 +167,9 @@ declare_features! (
     // RFC 1238
     (active, dropck_parametricity, "1.3.0", Some(28498)),
 
+    // Allows using the may_dangle attribute; RFC 1327
+    (active, dropck_eyepatch, "1.10.0", Some(34761)),
+
     // Allows the use of custom attributes; RFC 572
     (active, custom_attribute, "1.0.0", Some(29642)),
 
@@ -617,6 +620,11 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
            "unsafe_destructor_blind_to_params has unstable semantics \
             and may be removed in the future",
            cfg_fn!(dropck_parametricity))),
+    ("may_dangle",
+     Normal,
+     Gated("dropck_eyepatch",
+           "may_dangle has unstable semantics and may be removed in the future",
+           cfg_fn!(dropck_eyepatch))),
     ("unwind", Whitelisted, Gated("unwind_attributes", "#[unwind] is experimental",
                                   cfg_fn!(unwind_attributes))),
 
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 3c106970232cd..ee5e9a1d7d5dd 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1361,6 +1361,7 @@ impl<'a> State<'a> {
                 if comma {
                     try!(self.word_space(","))
                 }
+                try!(self.print_outer_attributes_inline(&lifetime_def.attrs));
                 try!(self.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds));
                 comma = true;
             }
@@ -2803,6 +2804,7 @@ impl<'a> State<'a> {
         try!(self.commasep(Inconsistent, &ints[..], |s, &idx| {
             if idx < generics.lifetimes.len() {
                 let lifetime_def = &generics.lifetimes[idx];
+                try!(s.print_outer_attributes_inline(&lifetime_def.attrs));
                 s.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds)
             } else {
                 let idx = idx - generics.lifetimes.len();
@@ -2816,6 +2818,7 @@ impl<'a> State<'a> {
     }
 
     pub fn print_ty_param(&mut self, param: &ast::TyParam) -> io::Result<()> {
+        try!(self.print_outer_attributes_inline(&param.attrs));
         try!(self.print_ident(param.ident));
         try!(self.print_bounds(":", &param.bounds));
         match param.default {
diff --git a/src/test/compile-fail/E0198.rs b/src/test/compile-fail/E0198.rs
new file mode 100644
index 0000000000000..892989cc6a080
--- /dev/null
+++ b/src/test/compile-fail/E0198.rs
@@ -0,0 +1,18 @@
+// Copyright 2016 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.
+
+#![feature(optin_builtin_traits)]
+
+struct Foo;
+
+unsafe impl !Clone for Foo { } //~ ERROR negative implementations are not unsafe [E0198]
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0199.rs b/src/test/compile-fail/E0199.rs
index 8bd3ffdf6f6ee..1a5cd1941a9d1 100644
--- a/src/test/compile-fail/E0199.rs
+++ b/src/test/compile-fail/E0199.rs
@@ -12,7 +12,8 @@
 
 struct Foo;
 
-unsafe impl !Clone for Foo { } //~ ERROR E0199
+trait Bar { }
+unsafe impl Bar for Foo { } //~ ERROR implementing the trait `Bar` is not unsafe [E0199]
 
 fn main() {
 }
diff --git a/src/test/compile-fail/feature-gate-may-dangle.rs b/src/test/compile-fail/feature-gate-may-dangle.rs
new file mode 100644
index 0000000000000..23f8ead0ca9dc
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-may-dangle.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 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 `may_dangle` is rejected if `dropck_eyepatch` feature gate is absent.
+
+#![feature(generic_param_attrs)]
+
+struct Pt<A>(A);
+impl<#[may_dangle] A> Drop for Pt<A> {
+    //~^ ERROR may_dangle has unstable semantics and may be removed in the future
+    //~| HELP add #![feature(dropck_eyepatch)] to the crate attributes to enable
+    fn drop(&mut self) { }
+}
diff --git a/src/test/run-pass/auxiliary/dropck_eyepatch_extern_crate.rs b/src/test/run-pass/auxiliary/dropck_eyepatch_extern_crate.rs
new file mode 100644
index 0000000000000..1266e589b127e
--- /dev/null
+++ b/src/test/run-pass/auxiliary/dropck_eyepatch_extern_crate.rs
@@ -0,0 +1,61 @@
+// Copyright 2016 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.
+
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+// The point of this test is to illustrate that the `#[may_dangle]`
+// attribute specifically allows, in the context of a type
+// implementing `Drop`, a generic parameter to be instantiated with a
+// lifetime that does not strictly outlive the owning type itself,
+// and that this attributes effects are preserved when importing
+// the type from another crate.
+//
+// See also dropck-eyepatch.rs for more information about the general
+// structure of the test.
+
+use std::cell::RefCell;
+
+pub trait Foo { fn foo(&self, _: &str); }
+
+pub struct Dt<A: Foo>(pub &'static str, pub A);
+pub struct Dr<'a, B:'a+Foo>(pub &'static str, pub &'a B);
+pub struct Pt<A,B: Foo>(pub &'static str, pub A, pub B);
+pub struct Pr<'a, 'b, B:'a+'b+Foo>(pub &'static str, pub &'a B, pub &'b B);
+pub struct St<A: Foo>(pub &'static str, pub A);
+pub struct Sr<'a, B:'a+Foo>(pub &'static str, pub &'a B);
+
+impl<A: Foo> Drop for Dt<A> {
+    fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
+}
+impl<'a, B: Foo> Drop for Dr<'a, B> {
+    fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
+}
+unsafe impl<#[may_dangle] A, B: Foo> Drop for Pt<A, B> {
+    // (unsafe to access self.1  due to #[may_dangle] on A)
+    fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
+}
+unsafe impl<#[may_dangle] 'a, 'b, B: Foo> Drop for Pr<'a, 'b, B> {
+    // (unsafe to access self.1 due to #[may_dangle] on 'a)
+    fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
+}
+
+impl Foo for RefCell<String> {
+    fn foo(&self, s: &str) {
+        let s2 = format!("{}|{}", *self.borrow(), s);
+        *self.borrow_mut() = s2;
+    }
+}
+
+impl<'a, T:Foo> Foo for &'a T {
+    fn foo(&self, s: &str) {
+        (*self).foo(s);
+    }
+}
diff --git a/src/test/run-pass/dropck-eyepatch-extern-crate.rs b/src/test/run-pass/dropck-eyepatch-extern-crate.rs
new file mode 100644
index 0000000000000..20f069f77ea15
--- /dev/null
+++ b/src/test/run-pass/dropck-eyepatch-extern-crate.rs
@@ -0,0 +1,48 @@
+// Copyright 2016 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.
+
+// aux-build:dropck_eyepatch_extern_crate.rs
+
+extern crate dropck_eyepatch_extern_crate as other;
+
+use other::{Dt,Dr,Pt,Pr,St,Sr};
+
+fn main() {
+    use std::cell::RefCell;
+
+    struct CheckOnDrop(RefCell<String>, &'static str);
+    impl Drop for CheckOnDrop {
+        fn drop(&mut self) { assert_eq!(*self.0.borrow(), self.1); }
+    }
+
+    let c_long;
+    let (c, dt, dr, pt, pr, st, sr)
+        : (CheckOnDrop, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
+    c_long = CheckOnDrop(RefCell::new("c_long".to_string()),
+                         "c_long|pr|pt|dr|dt");
+    c = CheckOnDrop(RefCell::new("c".to_string()),
+                    "c");
+
+    // No error: sufficiently long-lived state can be referenced in dtors
+    dt = Dt("dt", &c_long.0);
+    dr = Dr("dr", &c_long.0);
+
+    // No error: Drop impl asserts .1 (A and &'a _) are not accessed
+    pt = Pt("pt", &c.0, &c_long.0);
+    pr = Pr("pr", &c.0, &c_long.0);
+
+    // No error: St and Sr have no destructor.
+    st = St("st", &c.0);
+    sr = Sr("sr", &c.0);
+
+    println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
+    assert_eq!(*c_long.0.borrow(), "c_long");
+    assert_eq!(*c.0.borrow(), "c");
+}
diff --git a/src/test/run-pass/dropck-eyepatch-reorder.rs b/src/test/run-pass/dropck-eyepatch-reorder.rs
new file mode 100644
index 0000000000000..bbf8bb8c35238
--- /dev/null
+++ b/src/test/run-pass/dropck-eyepatch-reorder.rs
@@ -0,0 +1,89 @@
+// Copyright 2016 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.
+
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+// The point of this test is to test uses of `#[may_dangle]` attribute
+// where the formal declaration order (in the impl generics) does not
+// match the actual usage order (in the type instantiation).
+//
+// See also dropck-eyepatch.rs for more information about the general
+// structure of the test.
+
+trait Foo { fn foo(&self, _: &str); }
+
+struct Dt<A: Foo>(&'static str, A);
+struct Dr<'a, B:'a+Foo>(&'static str, &'a B);
+struct Pt<A: Foo, B: Foo>(&'static str, A, B);
+struct Pr<'a, 'b, B:'a+'b+Foo>(&'static str, &'a B, &'b B);
+struct St<A: Foo>(&'static str, A);
+struct Sr<'a, B:'a+Foo>(&'static str, &'a B);
+
+impl<A: Foo> Drop for Dt<A> {
+    fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
+}
+impl<'a, B: Foo> Drop for Dr<'a, B> {
+    fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
+}
+unsafe impl<B: Foo, #[may_dangle] A: Foo> Drop for Pt<A, B> {
+    // (unsafe to access self.1  due to #[may_dangle] on A)
+    fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
+}
+unsafe impl<'b, #[may_dangle] 'a, B: Foo> Drop for Pr<'a, 'b, B> {
+    // (unsafe to access self.1 due to #[may_dangle] on 'a)
+    fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
+}
+
+fn main() {
+    use std::cell::RefCell;
+
+    impl Foo for RefCell<String> {
+        fn foo(&self, s: &str) {
+            let s2 = format!("{}|{}", *self.borrow(), s);
+            *self.borrow_mut() = s2;
+        }
+    }
+
+    impl<'a, T:Foo> Foo for &'a T {
+        fn foo(&self, s: &str) {
+            (*self).foo(s);
+        }
+    }
+
+    struct CheckOnDrop(RefCell<String>, &'static str);
+    impl Drop for CheckOnDrop {
+        fn drop(&mut self) { assert_eq!(*self.0.borrow(), self.1); }
+    }
+
+    let c_long;
+    let (c, dt, dr, pt, pr, st, sr)
+        : (CheckOnDrop, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
+    c_long = CheckOnDrop(RefCell::new("c_long".to_string()),
+                         "c_long|pr|pt|dr|dt");
+    c = CheckOnDrop(RefCell::new("c".to_string()),
+                    "c");
+
+    // No error: sufficiently long-lived state can be referenced in dtors
+    dt = Dt("dt", &c_long.0);
+    dr = Dr("dr", &c_long.0);
+
+    // No error: Drop impl asserts .1 (A and &'a _) are not accessed
+    pt = Pt("pt", &c.0, &c_long.0);
+    pr = Pr("pr", &c.0, &c_long.0);
+
+    // No error: St and Sr have no destructor.
+    st = St("st", &c.0);
+    sr = Sr("sr", &c.0);
+
+    println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
+    assert_eq!(*c_long.0.borrow(), "c_long");
+    assert_eq!(*c.0.borrow(), "c");
+}
diff --git a/src/test/run-pass/dropck-eyepatch.rs b/src/test/run-pass/dropck-eyepatch.rs
new file mode 100644
index 0000000000000..4a09ba05dff5e
--- /dev/null
+++ b/src/test/run-pass/dropck-eyepatch.rs
@@ -0,0 +1,112 @@
+// Copyright 2016 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.
+
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+// The point of this test is to illustrate that the `#[may_dangle]`
+// attribute specifically allows, in the context of a type
+// implementing `Drop`, a generic parameter to be instantiated with a
+// lifetime that does not strictly outlive the owning type itself.
+//
+// Here we test that a model use of `#[may_dangle]` will compile and run.
+//
+// The illustration is made concrete by comparison with two variations
+// on the type with `#[may_dangle]`:
+//
+//   1. an analogous type that does not implement `Drop` (and thus
+//      should exhibit maximal flexibility with respect to dropck), and
+//
+//   2. an analogous type that does not use `#[may_dangle]` (and thus
+//      should exhibit the standard limitations imposed by dropck.
+//
+// The types in this file follow a pattern, {D,P,S}{t,r}, where:
+//
+// - D means "I implement Drop"
+//
+// - P means "I implement Drop but guarantee my (first) parameter is
+//     pure, i.e. not accessed from the destructor"; no other parameters
+//     are pure.
+//
+// - S means "I do not implement Drop"
+//
+// - t suffix is used when the first generic is a type
+//
+// - r suffix is used when the first generic is a lifetime.
+
+trait Foo { fn foo(&self, _: &str); }
+
+struct Dt<A: Foo>(&'static str, A);
+struct Dr<'a, B:'a+Foo>(&'static str, &'a B);
+struct Pt<A,B: Foo>(&'static str, A, B);
+struct Pr<'a, 'b, B:'a+'b+Foo>(&'static str, &'a B, &'b B);
+struct St<A: Foo>(&'static str, A);
+struct Sr<'a, B:'a+Foo>(&'static str, &'a B);
+
+impl<A: Foo> Drop for Dt<A> {
+    fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
+}
+impl<'a, B: Foo> Drop for Dr<'a, B> {
+    fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
+}
+unsafe impl<#[may_dangle] A, B: Foo> Drop for Pt<A, B> {
+    // (unsafe to access self.1  due to #[may_dangle] on A)
+    fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
+}
+unsafe impl<#[may_dangle] 'a, 'b, B: Foo> Drop for Pr<'a, 'b, B> {
+    // (unsafe to access self.1 due to #[may_dangle] on 'a)
+    fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
+}
+
+fn main() {
+    use std::cell::RefCell;
+
+    impl Foo for RefCell<String> {
+        fn foo(&self, s: &str) {
+            let s2 = format!("{}|{}", *self.borrow(), s);
+            *self.borrow_mut() = s2;
+        }
+    }
+
+    impl<'a, T:Foo> Foo for &'a T {
+        fn foo(&self, s: &str) {
+            (*self).foo(s);
+        }
+    }
+
+    struct CheckOnDrop(RefCell<String>, &'static str);
+    impl Drop for CheckOnDrop {
+        fn drop(&mut self) { assert_eq!(*self.0.borrow(), self.1); }
+    }
+
+    let c_long;
+    let (c, dt, dr, pt, pr, st, sr)
+        : (CheckOnDrop, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
+    c_long = CheckOnDrop(RefCell::new("c_long".to_string()),
+                         "c_long|pr|pt|dr|dt");
+    c = CheckOnDrop(RefCell::new("c".to_string()),
+                    "c");
+
+    // No error: sufficiently long-lived state can be referenced in dtors
+    dt = Dt("dt", &c_long.0);
+    dr = Dr("dr", &c_long.0);
+
+    // No error: Drop impl asserts .1 (A and &'a _) are not accessed
+    pt = Pt("pt", &c.0, &c_long.0);
+    pr = Pr("pr", &c.0, &c_long.0);
+
+    // No error: St and Sr have no destructor.
+    st = St("st", &c.0);
+    sr = Sr("sr", &c.0);
+
+    println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
+    assert_eq!(*c_long.0.borrow(), "c_long");
+    assert_eq!(*c.0.borrow(), "c");
+}
diff --git a/src/test/ui/dropck/auxiliary/dropck_eyepatch_extern_crate.rs b/src/test/ui/dropck/auxiliary/dropck_eyepatch_extern_crate.rs
new file mode 100644
index 0000000000000..1b00d88dcb3df
--- /dev/null
+++ b/src/test/ui/dropck/auxiliary/dropck_eyepatch_extern_crate.rs
@@ -0,0 +1,48 @@
+// Copyright 2016 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.
+
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+// This is a support file for ../dropck-eyepatch-extern-crate.rs
+//
+// The point of this test is to illustrate that the `#[may_dangle]`
+// attribute specifically allows, in the context of a type
+// implementing `Drop`, a generic parameter to be instantiated with a
+// lifetime that does not strictly outlive the owning type itself,
+// and that this attribute's effects are preserved when importing
+// the type from another crate.
+//
+// See also ../dropck-eyepatch.rs for more information about the general
+// structure of the test.
+
+use std::fmt;
+
+pub struct Dt<A: fmt::Debug>(pub &'static str, pub A);
+pub struct Dr<'a, B:'a+fmt::Debug>(pub &'static str, pub &'a B);
+pub struct Pt<A,B: fmt::Debug>(pub &'static str, pub A, pub B);
+pub struct Pr<'a, 'b, B:'a+'b+fmt::Debug>(pub &'static str, pub &'a B, pub &'b B);
+pub struct St<A: fmt::Debug>(pub &'static str, pub A);
+pub struct Sr<'a, B:'a+fmt::Debug>(pub &'static str, pub &'a B);
+
+impl<A: fmt::Debug> Drop for Dt<A> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+impl<'a, B: fmt::Debug> Drop for Dr<'a, B> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+unsafe impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
+    // (unsafe to access self.1  due to #[may_dangle] on A)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
+unsafe impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
+    // (unsafe to access self.1 due to #[may_dangle] on 'a)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
diff --git a/src/test/ui/dropck/dropck-eyepatch-extern-crate.rs b/src/test/ui/dropck/dropck-eyepatch-extern-crate.rs
new file mode 100644
index 0000000000000..5e200dbdbfa01
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch-extern-crate.rs
@@ -0,0 +1,55 @@
+// Copyright 2016 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.
+
+// aux-build:dropck_eyepatch_extern_crate.rs
+
+// The point of this test is to illustrate that the `#[may_dangle]`
+// attribute specifically allows, in the context of a type
+// implementing `Drop`, a generic parameter to be instantiated with a
+// lifetime that does not strictly outlive the owning type itself,
+// and that this attribute's effects are preserved when importing
+// the type from another crate.
+//
+// See also dropck-eyepatch.rs for more information about the general
+// structure of the test.
+
+extern crate dropck_eyepatch_extern_crate as other;
+
+use other::{Dt,Dr,Pt,Pr,St,Sr};
+
+fn main() {
+    use std::cell::Cell;
+    let c_long;
+    let (c, mut dt, mut dr, mut pt, mut pr, st, sr)
+        : (Cell<_>, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
+    c_long = Cell::new(1);
+    c = Cell::new(1);
+
+    // No error: sufficiently long-lived state can be referenced in dtors
+    dt = Dt("dt", &c_long);
+    dr = Dr("dr", &c_long);
+    // Error: destructor order imprecisely modelled
+    dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
+    dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
+
+    // No error: Drop impl asserts .1 (A and &'a _) are not accessed
+    pt = Pt("pt", &c, &c_long);
+    pr = Pr("pr", &c, &c_long);
+
+    // Error: Drop impl's assertion does not apply to `B` nor `&'b _`
+    pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
+    pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
+
+    // No error: St and Sr have no destructor.
+    st = St("st", &c);
+    sr = Sr("sr", &c);
+
+    println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
+}
diff --git a/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr b/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr
new file mode 100644
index 0000000000000..5d2096e8b07a4
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch-extern-crate.stderr
@@ -0,0 +1,46 @@
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-extern-crate.rs:39:20
+   |
+39 |     dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
+   |                    ^ does not live long enough
+...
+55 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-extern-crate.rs:40:20
+   |
+40 |     dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
+   |                    ^ does not live long enough
+...
+55 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-extern-crate.rs:47:29
+   |
+47 |     pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
+   |                             ^ does not live long enough
+...
+55 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-extern-crate.rs:48:29
+   |
+48 |     pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
+   |                             ^ does not live long enough
+...
+55 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.rs b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.rs
new file mode 100644
index 0000000000000..f92c8703dc927
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.rs
@@ -0,0 +1,46 @@
+// Copyright 2016 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.
+
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+// This test ensures that a use of `#[may_dangle]` is rejected if
+// it is not attached to an `unsafe impl`.
+
+use std::fmt;
+
+struct Dt<A: fmt::Debug>(&'static str, A);
+struct Dr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
+struct Pt<A,B: fmt::Debug>(&'static str, A, B);
+struct Pr<'a, 'b, B:'a+'b+fmt::Debug>(&'static str, &'a B, &'b B);
+struct St<A: fmt::Debug>(&'static str, A);
+struct Sr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
+
+impl<A: fmt::Debug> Drop for Dt<A> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+impl<'a, B: fmt::Debug> Drop for Dr<'a, B> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
+    //~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
+
+    // (unsafe to access self.1  due to #[may_dangle] on A)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
+impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
+    //~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
+
+    // (unsafe to access self.1 due to #[may_dangle] on 'a)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
+
+fn main() {
+}
diff --git a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr
new file mode 100644
index 0000000000000..c53cf020a9bc5
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr
@@ -0,0 +1,14 @@
+error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
+  --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:32:1
+   |
+32 | impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
+   | ^
+
+error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
+  --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:38:1
+   |
+38 | impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
+   | ^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/dropck/dropck-eyepatch-reorder.rs b/src/test/ui/dropck/dropck-eyepatch-reorder.rs
new file mode 100644
index 0000000000000..68b0ff3b5f096
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch-reorder.rs
@@ -0,0 +1,73 @@
+// Copyright 2016 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.
+
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+// The point of this test is to test uses of `#[may_dangle]` attribute
+// where the formal declaration order (in the impl generics) does not
+// match the actual usage order (in the type instantiation).
+//
+// See also dropck-eyepatch.rs for more information about the general
+// structure of the test.
+
+use std::fmt;
+
+struct Dt<A: fmt::Debug>(&'static str, A);
+struct Dr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
+struct Pt<A: fmt::Debug, B: fmt::Debug>(&'static str, A, B);
+struct Pr<'a, 'b, B:'a+'b+fmt::Debug>(&'static str, &'a B, &'b B);
+struct St<A: fmt::Debug>(&'static str, A);
+struct Sr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
+
+impl<A: fmt::Debug> Drop for Dt<A> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+impl<'a, B: fmt::Debug> Drop for Dr<'a, B> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+unsafe impl<B: fmt::Debug, #[may_dangle] A: fmt::Debug> Drop for Pt<A, B> {
+    // (unsafe to access self.1  due to #[may_dangle] on A)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
+unsafe impl<'b, #[may_dangle] 'a, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
+    // (unsafe to access self.1 due to #[may_dangle] on 'a)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
+
+fn main() {
+    use std::cell::Cell;
+    let c_long;
+    let (c, mut dt, mut dr, mut pt, mut pr, st, sr)
+        : (Cell<_>, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
+    c_long = Cell::new(1);
+    c = Cell::new(1);
+
+    // No error: sufficiently long-lived state can be referenced in dtors
+    dt = Dt("dt", &c_long);
+    dr = Dr("dr", &c_long);
+    // Error: destructor order imprecisely modelled
+    dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
+    dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
+
+    // No error: Drop impl asserts .1 (A and &'a _) are not accessed
+    pt = Pt("pt", &c, &c_long);
+    pr = Pr("pr", &c, &c_long);
+
+    // Error: Drop impl's assertion does not apply to `B` nor `&'b _`
+    pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
+    pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
+
+    // No error: St and Sr have no destructor.
+    st = St("st", &c);
+    sr = Sr("sr", &c);
+
+    println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
+}
diff --git a/src/test/ui/dropck/dropck-eyepatch-reorder.stderr b/src/test/ui/dropck/dropck-eyepatch-reorder.stderr
new file mode 100644
index 0000000000000..33b18f6f02e0f
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch-reorder.stderr
@@ -0,0 +1,46 @@
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-reorder.rs:57:20
+   |
+57 |     dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
+   |                    ^ does not live long enough
+...
+73 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-reorder.rs:58:20
+   |
+58 |     dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
+   |                    ^ does not live long enough
+...
+73 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-reorder.rs:65:29
+   |
+65 |     pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
+   |                             ^ does not live long enough
+...
+73 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch-reorder.rs:66:29
+   |
+66 |     pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
+   |                             ^ does not live long enough
+...
+73 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/dropck/dropck-eyepatch.rs b/src/test/ui/dropck/dropck-eyepatch.rs
new file mode 100644
index 0000000000000..e442fade19730
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch.rs
@@ -0,0 +1,96 @@
+// Copyright 2016 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.
+
+#![feature(generic_param_attrs)]
+#![feature(dropck_eyepatch)]
+
+// The point of this test is to illustrate that the `#[may_dangle]`
+// attribute specifically allows, in the context of a type
+// implementing `Drop`, a generic parameter to be instantiated with a
+// lifetime that does not strictly outlive the owning type itself.
+//
+// Here we test that only the expected errors are issued.
+//
+// The illustration is made concrete by comparison with two variations
+// on the type with `#[may_dangle]`:
+//
+//   1. an analogous type that does not implement `Drop` (and thus
+//      should exhibit maximal flexibility with respect to dropck), and
+//
+//   2. an analogous type that does not use `#[may_dangle]` (and thus
+//      should exhibit the standard limitations imposed by dropck.
+//
+// The types in this file follow a pattern, {D,P,S}{t,r}, where:
+//
+// - D means "I implement Drop"
+//
+// - P means "I implement Drop but guarantee my (first) parameter is
+//     pure, i.e. not accessed from the destructor"; no other parameters
+//     are pure.
+//
+// - S means "I do not implement Drop"
+//
+// - t suffix is used when the first generic is a type
+//
+// - r suffix is used when the first generic is a lifetime.
+
+use std::fmt;
+
+struct Dt<A: fmt::Debug>(&'static str, A);
+struct Dr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
+struct Pt<A,B: fmt::Debug>(&'static str, A, B);
+struct Pr<'a, 'b, B:'a+'b+fmt::Debug>(&'static str, &'a B, &'b B);
+struct St<A: fmt::Debug>(&'static str, A);
+struct Sr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
+
+impl<A: fmt::Debug> Drop for Dt<A> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+impl<'a, B: fmt::Debug> Drop for Dr<'a, B> {
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
+}
+unsafe impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
+    // (unsafe to access self.1  due to #[may_dangle] on A)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
+unsafe impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
+    // (unsafe to access self.1 due to #[may_dangle] on 'a)
+    fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
+}
+
+fn main() {
+    use std::cell::Cell;
+    let c_long;
+    let (c, mut dt, mut dr, mut pt, mut pr, st, sr)
+        : (Cell<_>, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
+    c_long = Cell::new(1);
+    c = Cell::new(1);
+
+    // No error: sufficiently long-lived state can be referenced in dtors
+    dt = Dt("dt", &c_long);
+    dr = Dr("dr", &c_long);
+    // Error: destructor order imprecisely modelled
+    dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
+    dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
+
+    // No error: Drop impl asserts .1 (A and &'a _) are not accessed
+    pt = Pt("pt", &c, &c_long);
+    pr = Pr("pr", &c, &c_long);
+
+    // Error: Drop impl's assertion does not apply to `B` nor `&'b _`
+    pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
+    pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
+
+    // No error: St and Sr have no destructor.
+    st = St("st", &c);
+    sr = Sr("sr", &c);
+
+    println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
+}
diff --git a/src/test/ui/dropck/dropck-eyepatch.stderr b/src/test/ui/dropck/dropck-eyepatch.stderr
new file mode 100644
index 0000000000000..75e612ca9c8f1
--- /dev/null
+++ b/src/test/ui/dropck/dropck-eyepatch.stderr
@@ -0,0 +1,46 @@
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch.rs:80:20
+   |
+80 |     dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
+   |                    ^ does not live long enough
+...
+96 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch.rs:81:20
+   |
+81 |     dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
+   |                    ^ does not live long enough
+...
+96 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch.rs:88:29
+   |
+88 |     pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
+   |                             ^ does not live long enough
+...
+96 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: `c` does not live long enough
+  --> $DIR/dropck-eyepatch.rs:89:29
+   |
+89 |     pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
+   |                             ^ does not live long enough
+...
+96 | }
+   | - borrowed value dropped before borrower
+   |
+   = note: values in a scope are dropped in the opposite order they are created
+
+error: aborting due to 4 previous errors
+