Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes NLL: error from URL crate #47917

Merged
merged 6 commits into from
Feb 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 73 additions & 7 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will take an extra argument:

erased_drop_place_ty: Ty<'gcx>,

Note that it belongs in the global arena ('gcx) -- this is possible because its regions have been erased.

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
Expand Down
33 changes: 33 additions & 0 deletions src/test/run-pass/issue-47703-1.rs
Original file line number Diff line number Diff line change
@@ -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() {}
28 changes: 28 additions & 0 deletions src/test/run-pass/issue-47703.rs
Original file line number Diff line number Diff line change
@@ -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() {}