about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_typeck/check/dropck.rs375
-rw-r--r--src/librustc_typeck/check/regionck.rs2
-rw-r--r--src/test/compile-fail/dropck_misc_variants.rs47
-rw-r--r--src/test/run-pass/issue-24086.rs29
-rw-r--r--src/test/run-pass/issue-26641.rs15
-rw-r--r--src/test/run-pass/issue-27240.rs33
6 files changed, 309 insertions, 192 deletions
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 38207354792..7cb9b6c22b9 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -268,7 +268,6 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
         typ,
         span,
         scope,
-        0,
         0);
     match result {
         Ok(()) => {}
@@ -311,6 +310,7 @@ enum Error<'tcx> {
     Overflow(TypeContext, ty::Ty<'tcx>),
 }
 
+#[derive(Copy, Clone)]
 enum TypeContext {
     Root,
     EnumVariant {
@@ -331,217 +331,209 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>(
     rcx: &mut Rcx<'a, 'tcx>,
     breadcrumbs: &mut Vec<Ty<'tcx>>,
     context: TypeContext,
-    ty_root: ty::Ty<'tcx>,
+    ty: Ty<'tcx>,
     span: Span,
     scope: region::CodeExtent,
-    depth: usize,
-    xref_depth: usize) -> Result<(), Error<'tcx>>
+    depth: usize) -> Result<(), Error<'tcx>>
 {
     // Issue #22443: Watch out for overflow. While we are careful to
     // handle regular types properly, non-regular ones cause problems.
     let recursion_limit = rcx.tcx().sess.recursion_limit.get();
-    if xref_depth >= recursion_limit {
-        return Err(Error::Overflow(context, ty_root))
+    if depth / 4 >= recursion_limit {
+        return Err(Error::Overflow(context, ty))
     }
 
-    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();
+    // FIXME(arielb1): don't be O(n^2)
+    if breadcrumbs.contains(&ty) {
+        debug!("iterate_over_potentially_unsafe_regions_in_type \
+               {}ty: {} scope: {:?} - cached",
+               (0..depth).map(|_| ' ').collect::<String>(),
+               ty, scope);
+        return Ok(()); // we already visited this type
+    }
+    breadcrumbs.push(ty);
+    debug!("iterate_over_potentially_unsafe_regions_in_type \
+           {}ty: {} scope: {:?}",
+           (0..depth).map(|_| ' ').collect::<String>(),
+           ty, scope);
+
+    // FIXME(arielb1): move into has_dtor_of_interest
+    let dtor_kind = match ty.sty {
+        ty::TyEnum(def_id, _) |
+        ty::TyStruct(def_id, _) => {
+            let destructor_for_type = rcx.tcx().destructor_for_type.borrow();
+            match destructor_for_type.get(&def_id) {
+                Some(def_id) => DtorKind::KnownDropMethod(*def_id),
+                None => DtorKind::PureRecur,
+            }
+        }
+        ty::TyTrait(..) | ty::TyProjection(..) => DtorKind::Unknown,
+        _ => DtorKind::PureRecur,
+    };
+
 
-    let xref_depth_orig = xref_depth;
+    // 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).
+    if has_dtor_of_interest(rcx.tcx(), dtor_kind, ty, span) {
+        debug!("iterate_over_potentially_unsafe_regions_in_type \
+                {}ty: {} - is a dtorck type!",
+               (0..depth).map(|_| ' ').collect::<String>(),
+               ty);
+
+        // If `ty` is a dtorck type, then we must ensure that all
+        // borrowed data reachable via `ty` 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)),
+            };
 
-    while let Some(typ) = walker.next() {
-        // Avoid recursing forever.
-        if breadcrumbs.contains(&typ) {
-            continue;
+        regionck::type_must_outlive(rcx,
+                                    infer::SubregionOrigin::SafeDestructor(span),
+                                    ty,
+                                    parent_region);
+
+        return Ok(());
+    }
+
+    debug!("iterate_over_potentially_unsafe_regions_in_type \
+           {}ty: {} scope: {:?} - checking interior",
+           (0..depth).map(|_| ' ').collect::<String>(),
+           ty, scope);
+
+    // We still need to ensure all referenced data is safe.
+    match ty.sty {
+        ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) |
+        ty::TyFloat(_) | ty::TyStr => {
+            // primitive - definitely safe
+            Ok(())
         }
-        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, xref_depth) = match typ.sty {
-            ty::TyStruct(struct_did, substs) => {
-                if opt_phantom_data_def_id == Some(struct_did) {
-                    let item_type = rcx.tcx().lookup_item_type(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, new_typ);
-                    (new_typ, xref_depth_orig + 1)
-                } else {
-                    (typ, xref_depth_orig)
-                }
-            }
 
-            // Note: When TyBox is removed from compiler, the
-            // definition of `Box<T>` must carry a PhantomData that
-            // puts us into the previous case.
-            ty::TyBox(new_typ) => {
-                debug!("replacing TyBox {:?} with {:?}",
-                       typ, new_typ);
-                (new_typ, xref_depth_orig + 1)
-            }
+        ty::TyBox(ity) | ty::TyArray(ity, _) | ty::TySlice(ity) => {
+            // single-element containers, behave like their element
+            iterate_over_potentially_unsafe_regions_in_type(
+                rcx, breadcrumbs, context, ity, span, scope, depth+1)
+        }
+
+        ty::TyStruct(did, substs) if Some(did) == opt_phantom_data_def_id => {
+            // PhantomData<T> - behaves identically to T
+            let ity = *substs.types.get(subst::TypeSpace, 0);
+            iterate_over_potentially_unsafe_regions_in_type(
+                rcx, breadcrumbs, context, ity, span, scope, depth+1)
+        }
 
-            _ => {
-                (typ, xref_depth_orig)
+        ty::TyStruct(did, substs) => {
+            let fields = rcx.tcx().lookup_struct_fields(did);
+            for field in &fields {
+                let field_type = rcx.tcx().lookup_field_type(did,
+                                                             field.id,
+                                                             substs);
+                try!(iterate_over_potentially_unsafe_regions_in_type(
+                    rcx,
+                    breadcrumbs,
+                    TypeContext::Struct {
+                        def_id: did,
+                        field: field.name,
+                    },
+                    rcx.fcx.resolve_type_vars_if_possible(
+                        rcx.fcx.normalize_associated_types_in(span, &field_type)),
+                    span,
+                    scope,
+                    depth+1))
             }
-        };
-
-        let dtor_kind = match typ.sty {
-            ty::TyEnum(def_id, _) |
-            ty::TyStruct(def_id, _) => {
-                match destructor_for_type.get(&def_id) {
-                    Some(def_id) => DtorKind::KnownDropMethod(*def_id),
-                    None => DtorKind::PureRecur,
+            Ok(())
+        }
+
+        ty::TyEnum(did, substs) => {
+            let all_variant_info =
+                rcx.tcx().substd_enum_variants(did, substs);
+            for variant_info in &all_variant_info {
+                for (i, arg_type) in variant_info.args.iter().enumerate() {
+                    try!(iterate_over_potentially_unsafe_regions_in_type(
+                        rcx,
+                        breadcrumbs,
+                        TypeContext::EnumVariant {
+                            def_id: did,
+                            variant: variant_info.name,
+                            arg_index: i,
+                        },
+                        rcx.fcx.resolve_type_vars_if_possible(
+                            rcx.fcx.normalize_associated_types_in(span, arg_type)),
+                        span,
+                        scope,
+                        depth+1));
                 }
             }
-            ty::TyTrait(ref ty_trait) => {
-                DtorKind::Unknown(ty_trait.bounds.clone())
-            }
-            _ => DtorKind::PureRecur,
-        };
-
-        debug!("iterate_over_potentially_unsafe_regions_in_type \
-                {}typ: {} scope: {:?} xref: {}",
-               (0..depth).map(|_| ' ').collect::<String>(),
-               typ, scope, xref_depth);
+            Ok(())
+        }
 
-        // 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).
-
-        if has_dtor_of_interest(rcx.tcx(), dtor_kind, typ, span) {
-            // 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)),
-                };
-
-            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::TyStruct(struct_did, substs) => {
-                    debug!("typ: {:?} is struct; traverse structure and not type-expression",
-                           typ);
-                    // Don't recurse; we extract type's substructure,
-                    // so do not process subparts of type expression.
-                    walker.skip_current_subtree();
-
-                    let fields =
-                        rcx.tcx().lookup_struct_fields(struct_did);
-                    for field in &fields {
-                        let field_type = rcx.tcx().lookup_field_type(struct_did,
-                                                                     field.id,
-                                                                     substs);
-                        try!(iterate_over_potentially_unsafe_regions_in_type(
-                            rcx,
-                            breadcrumbs,
-                            TypeContext::Struct {
-                                def_id: struct_did,
-                                field: field.name,
-                            },
-                            field_type,
-                            span,
-                            scope,
-                            depth+1,
-                            xref_depth))
-                    }
-                }
+        ty::TyTuple(ref tys) |
+        ty::TyClosure(_, box ty::ClosureSubsts { upvar_tys: ref tys, .. }) => {
+            for ty in tys {
+                try!(iterate_over_potentially_unsafe_regions_in_type(
+                    rcx, breadcrumbs, context, ty, span, scope, depth+1))
+            }
+            Ok(())
+        }
 
-                ty::TyEnum(enum_did, substs) => {
-                    debug!("typ: {:?} is enum; traverse structure and not type-expression",
-                           typ);
-                    // Don't recurse; we extract type's substructure,
-                    // so do not process subparts of type expression.
-                    walker.skip_current_subtree();
-
-                    let all_variant_info =
-                        rcx.tcx().substd_enum_variants(enum_did, substs);
-                    for variant_info in &all_variant_info {
-                        for (i, arg_type) in variant_info.args.iter().enumerate() {
-                            try!(iterate_over_potentially_unsafe_regions_in_type(
-                                rcx,
-                                breadcrumbs,
-                                TypeContext::EnumVariant {
-                                    def_id: enum_did,
-                                    variant: variant_info.name,
-                                    arg_index: i,
-                                },
-                                *arg_type,
-                                span,
-                                scope,
-                                depth+1,
-                                xref_depth));
-                        }
-                    }
-                }
+        ty::TyRawPtr(..) | ty::TyRef(..) | ty::TyParam(..) => {
+            // these always come with a witness of liveness (references
+            // explicitly, pointers implicitly, parameters by the
+            // caller).
+            Ok(())
+        }
 
-                ty::TyRef(..) | ty::TyRawPtr(_) | ty::TyBareFn(..) => {
-                    // Don't recurse, since references, pointers,
-                    // and bare functions don't own instances
-                    // of the types appearing within them.
-                    walker.skip_current_subtree();
-                }
-                _ => {}
-            };
+        ty::TyBareFn(..) => {
+            // FIXME(#26656): this type is always destruction-safe, but
+            // it implicitly witnesses Self: Fn, which can be false.
+            Ok(())
+        }
 
-            // 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.
+        ty::TyInfer(..) | ty::TyError => {
+            rcx.tcx().sess.delay_span_bug(span, "unresolved type in regionck");
+            Ok(())
         }
-    }
 
-    return Ok(());
+        // these are always dtorck
+        ty::TyTrait(..) | ty::TyProjection(_) => unreachable!(),
+    }
 }
 
-enum DtorKind<'tcx> {
+enum DtorKind {
     // Type has an associated drop method with this def id
     KnownDropMethod(ast::DefId),
 
@@ -552,7 +544,7 @@ enum DtorKind<'tcx> {
 
     // Type may have impure destructor that is unknown;
     // e.g. `Box<Trait+'a>`
-    Unknown(ty::ExistentialBounds<'tcx>),
+    Unknown,
 }
 
 fn has_dtor_of_interest<'tcx>(tcx: &ty::ctxt<'tcx>,
@@ -567,7 +559,10 @@ fn has_dtor_of_interest<'tcx>(tcx: &ty::ctxt<'tcx>,
             debug!("typ: {:?} has no dtor, and thus is uninteresting",
                    typ);
         }
-        DtorKind::Unknown(bounds) => {
+        DtorKind::Unknown => {
+            debug!("trait: {:?} is interesting", typ);
+            has_dtor_of_interest = true;
+/*
             match bounds.region_bound {
                 ty::ReStatic => {
                     debug!("trait: {:?} has 'static bound, and thus is uninteresting",
@@ -580,11 +575,9 @@ fn has_dtor_of_interest<'tcx>(tcx: &ty::ctxt<'tcx>,
                     has_dtor_of_interest = false;
                 }
                 r => {
-                    debug!("trait: {:?} has non-static bound: {:?}; assumed interesting",
-                           typ, r);
-                    has_dtor_of_interest = true;
                 }
             }
+*/
         }
         DtorKind::KnownDropMethod(dtor_method_did) => {
             let impl_did = tcx.impl_of_method(dtor_method_did)
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index c6f543210ad..aeac38dab90 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -174,7 +174,7 @@ pub fn regionck_ensure_component_tys_wf<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 // INTERNALS
 
 pub struct Rcx<'a, 'tcx: 'a> {
-    fcx: &'a FnCtxt<'a, 'tcx>,
+    pub fcx: &'a FnCtxt<'a, 'tcx>,
 
     region_bound_pairs: Vec<(ty::Region, GenericKind<'tcx>)>,
 
diff --git a/src/test/compile-fail/dropck_misc_variants.rs b/src/test/compile-fail/dropck_misc_variants.rs
new file mode 100644
index 00000000000..ee957f20d00
--- /dev/null
+++ b/src/test/compile-fail/dropck_misc_variants.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.
+
+// check that dropck does the right thing with misc. Ty variants
+
+use std::fmt;
+struct NoisyDrop<T: fmt::Debug>(T);
+impl<T: fmt::Debug> Drop for NoisyDrop<T> {
+    fn drop(&mut self) {
+        let _ = vec!["0wned"];
+        println!("dropping {:?}", self.0)
+    }
+}
+
+trait Associator {
+    type As;
+}
+impl<T: fmt::Debug> Associator for T {
+    type As = NoisyDrop<T>;
+}
+struct Wrap<A: Associator>(<A as Associator>::As);
+
+fn projection() {
+    let (_w, bomb);
+    bomb = vec![""];
+    _w = Wrap::<&[&str]>(NoisyDrop(&bomb));
+    //~^ ERROR `bomb` does not live long enough
+}
+
+fn closure() {
+    let (_w,v);
+    v = vec![""];
+    _w = {
+        let u = NoisyDrop(&v);
+        //~^ ERROR `v` does not live long enough
+        move || u.0.len()
+    };
+}
+
+fn main() { closure(); projection() }
diff --git a/src/test/run-pass/issue-24086.rs b/src/test/run-pass/issue-24086.rs
new file mode 100644
index 00000000000..0a8324eafe2
--- /dev/null
+++ b/src/test/run-pass/issue-24086.rs
@@ -0,0 +1,29 @@
+// 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.
+
+pub struct Registry<'a> {
+    listener: &'a mut (),
+}
+
+pub struct Listener<'a> {
+    pub announce: Option<Box<FnMut(&mut Registry) + 'a>>,
+    pub remove: Option<Box<FnMut(&mut Registry) + 'a>>,
+}
+
+impl<'a> Drop for Registry<'a> {
+    fn drop(&mut self) {}
+}
+
+fn main() {
+    let mut registry_listener = Listener {
+        announce: None,
+        remove: None,
+    };
+}
diff --git a/src/test/run-pass/issue-26641.rs b/src/test/run-pass/issue-26641.rs
new file mode 100644
index 00000000000..16f1d4626fb
--- /dev/null
+++ b/src/test/run-pass/issue-26641.rs
@@ -0,0 +1,15 @@
+// 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.
+
+struct Parser<'a>(Box<FnMut(Parser) + 'a>);
+
+fn main() {
+    let _x = Parser(Box::new(|_|{}));
+}
diff --git a/src/test/run-pass/issue-27240.rs b/src/test/run-pass/issue-27240.rs
new file mode 100644
index 00000000000..4e341d31172
--- /dev/null
+++ b/src/test/run-pass/issue-27240.rs
@@ -0,0 +1,33 @@
+// 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::fmt;
+struct NoisyDrop<T: fmt::Debug>(T);
+impl<T: fmt::Debug> Drop for NoisyDrop<T> {
+    fn drop(&mut self) {}
+}
+
+struct Bar<T: fmt::Debug>([*const NoisyDrop<T>; 2]);
+
+fn fine() {
+    let (u,b);
+    u = vec![43];
+    b = Bar([&NoisyDrop(&u), &NoisyDrop(&u)]);
+}
+
+struct Bar2<T: fmt::Debug>(*const NoisyDrop<T>, *const NoisyDrop<T>);
+
+fn lolwut() {
+    let (u,v);
+    u = vec![43];
+    v = Bar2(&NoisyDrop(&u), &NoisyDrop(&u));
+}
+
+fn main() { fine(); lolwut() }