about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-02-17 05:53:55 +0000
committerbors <bors@rust-lang.org>2018-02-17 05:53:55 +0000
commit507a46a4aa67555cd9b60ea2852f6b8cc681217b (patch)
treefff7d1b651f9c6a1b7cba389d5856a8468a28029 /src
parent554fe71ac6172ce9c0baeabbfd692280dcc893fe (diff)
parent98904c2ecc580b27f87e43dbe3e7f6995a2ec3a2 (diff)
downloadrust-507a46a4aa67555cd9b60ea2852f6b8cc681217b.tar.gz
rust-507a46a4aa67555cd9b60ea2852f6b8cc681217b.zip
Auto merge of #47917 - davidtwco:issue-47703, r=nikomatsakis
Fixes NLL: error from URL crate

Fixes #47703.

r? @nikomatsakis
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/borrow_check/mod.rs80
-rw-r--r--src/test/run-pass/issue-47703-1.rs33
-rw-r--r--src/test/run-pass/issue-47703.rs28
3 files changed, 134 insertions, 7 deletions
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index c4df7349391..650f99828ae 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -463,13 +463,20 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
                 target: _,
                 unwind: _,
             } => {
-                self.access_place(
-                    ContextKind::Drop.new(loc),
-                    (drop_place, span),
-                    (Deep, Write(WriteKind::StorageDeadOrDrop)),
-                    LocalMutationIsAllowed::Yes,
-                    flow_state,
-                );
+                let gcx = self.tcx.global_tcx();
+
+                // Compute the type with accurate region information.
+                let drop_place_ty = drop_place.ty(self.mir, self.tcx);
+
+                // Erase the regions.
+                let drop_place_ty = self.tcx.erase_regions(&drop_place_ty).to_ty(self.tcx);
+
+                // "Lift" into the gcx -- once regions are erased, this type should be in the
+                // global arenas; this "lift" operation basically just asserts that is true, but
+                // that is useful later.
+                let drop_place_ty = gcx.lift(&drop_place_ty).unwrap();
+
+                self.visit_terminator_drop(loc, term, flow_state, drop_place, drop_place_ty, span);
             }
             TerminatorKind::DropAndReplace {
                 location: ref drop_place,
@@ -717,6 +724,65 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
              self.tcx.sess.opts.debugging_opts.two_phase_beyond_autoref)
     }
 
+    /// Invokes `access_place` as appropriate for dropping the value
+    /// at `drop_place`. Note that the *actual* `Drop` in the MIR is
+    /// always for a variable (e.g., `Drop(x)`) -- but we recursively
+    /// break this variable down into subpaths (e.g., `Drop(x.foo)`)
+    /// to indicate more precisely which fields might actually be
+    /// accessed by a destructor.
+    fn visit_terminator_drop(
+        &mut self,
+        loc: Location,
+        term: &Terminator<'tcx>,
+        flow_state: &Flows<'cx, 'gcx, 'tcx>,
+        drop_place: &Place<'tcx>,
+        erased_drop_place_ty: ty::Ty<'gcx>,
+        span: Span,
+    ) {
+        match erased_drop_place_ty.sty {
+            // When a struct is being dropped, we need to check
+            // whether it has a destructor, if it does, then we can
+            // call it, if it does not then we need to check the
+            // individual fields instead. This way if `foo` has a
+            // destructor but `bar` does not, we will only check for
+            // borrows of `x.foo` and not `x.bar`. See #47703.
+            ty::TyAdt(def, substs) if def.is_struct() && !def.has_dtor(self.tcx) => {
+                for (index, field) in def.all_fields().enumerate() {
+                    let gcx = self.tcx.global_tcx();
+                    let field_ty = field.ty(gcx, substs);
+                    let field_ty = gcx.normalize_associated_type_in_env(&field_ty, self.param_env);
+                    let place = drop_place.clone().field(Field::new(index), field_ty);
+
+                    self.visit_terminator_drop(
+                        loc,
+                        term,
+                        flow_state,
+                        &place,
+                        field_ty,
+                        span,
+                    );
+                }
+            },
+            _ => {
+                // We have now refined the type of the value being
+                // dropped (potentially) to just the type of a
+                // subfield; so check whether that field's type still
+                // "needs drop". If so, we assume that the destructor
+                // may access any data it likes (i.e., a Deep Write).
+                let gcx = self.tcx.global_tcx();
+                if erased_drop_place_ty.needs_drop(gcx, self.param_env) {
+                    self.access_place(
+                        ContextKind::Drop.new(loc),
+                        (drop_place, span),
+                        (Deep, Write(WriteKind::StorageDeadOrDrop)),
+                        LocalMutationIsAllowed::Yes,
+                        flow_state,
+                    );
+                }
+            },
+        }
+    }
+
     /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
     /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
     /// place is initialized and (b) it is not borrowed in some way that would prevent this
diff --git a/src/test/run-pass/issue-47703-1.rs b/src/test/run-pass/issue-47703-1.rs
new file mode 100644
index 00000000000..facdee5cc17
--- /dev/null
+++ b/src/test/run-pass/issue-47703-1.rs
@@ -0,0 +1,33 @@
+// Copyright 2012 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(nll)]
+
+struct AtomicRefMut<'a> {
+    value: &'a mut i32,
+    borrow: AtomicBorrowRefMut,
+}
+
+struct AtomicBorrowRefMut {
+}
+
+impl Drop for AtomicBorrowRefMut {
+    fn drop(&mut self) {
+    }
+}
+
+fn map(orig: AtomicRefMut) -> AtomicRefMut {
+    AtomicRefMut {
+        value: orig.value,
+        borrow: orig.borrow,
+    }
+}
+
+fn main() {}
diff --git a/src/test/run-pass/issue-47703.rs b/src/test/run-pass/issue-47703.rs
new file mode 100644
index 00000000000..2146986377a
--- /dev/null
+++ b/src/test/run-pass/issue-47703.rs
@@ -0,0 +1,28 @@
+// Copyright 2012 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(nll)]
+
+struct MyStruct<'a> {
+    field: &'a mut (),
+    field2: WithDrop
+}
+
+struct WithDrop;
+
+impl Drop for WithDrop {
+    fn drop(&mut self) {}
+}
+
+impl<'a> MyStruct<'a> {
+    fn consume(self) -> &'a mut () { self.field }
+}
+
+fn main() {}