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

Fix spurious error when a Drop local has an assignment in a loop #102078

Closed
wants to merge 1 commit into from
Closed
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
6 changes: 6 additions & 0 deletions compiler/rustc_borrowck/src/def_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ pub enum DefUse {
Def,
Use,
Drop,
/// This is only created for a `DropAndReplace` terminator.
/// It needs special handling because represents a `Drop` immediately followed
/// by a `Def`, at the same MIR location.
DropAndDef,
}

pub fn categorize(context: PlaceContext) -> Option<DefUse> {
Expand All @@ -31,6 +35,8 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
PlaceContext::NonUse(NonUseContext::StorageLive) |
PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def),

PlaceContext::MutatingUse(MutatingUseContext::DropAndReplace) => Some(DefUse::DropAndDef),

///////////////////////////////////////////////////////////////////////////
// REGULAR USES
//
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/find_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ impl<'cx, 'tcx> Visitor<'tcx> for DefUseVisitor<'cx, 'tcx> {
Some(DefUse::Def) => Some(DefUseResult::Def),
Some(DefUse::Use) => Some(DefUseResult::UseLive { local }),
Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }),
// This makes some diagnostics nicer - we want to indicate
// that a variable is being kept alive because it can be accessed
// by this drop.
Some(DefUse::DropAndDef) => Some(DefUseResult::UseDrop { local }),
None => None,
};
}
Expand Down
20 changes: 19 additions & 1 deletion compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub(crate) struct LocalUseMap {
/// defined in `x = y` but not `y`; that first def is the head of
/// a linked list that lets you enumerate all places the variable
/// is assigned.
///
/// Note that a Local can have *both* a definition and a drop
/// at the same point - this occurs with `DropAndReplace` terminators.
first_def_at: IndexVec<Local, Option<AppearanceIndex>>,

/// Head of a linked list of **uses** of each variable -- use in
Expand All @@ -35,6 +38,9 @@ pub(crate) struct LocalUseMap {
/// Head of a linked list of **drops** of each variable -- these
/// are a special category of uses corresponding to the drop that
/// we add for each local variable.
///
/// Note that a Local can have *both* a definition and a drop
/// at the same point - this occurs with `DropAndReplace` terminators.
first_drop_at: IndexVec<Local, Option<AppearanceIndex>>,

appearances: IndexVec<AppearanceIndex, Appearance>,
Expand Down Expand Up @@ -163,7 +169,19 @@ impl Visitor<'_> for LocalUseMapBuild<'_> {
Some(DefUse::Def) => self.insert_def(local, location),
Some(DefUse::Use) => self.insert_use(local, location),
Some(DefUse::Drop) => self.insert_drop(local, location),
_ => (),
// This counts as *both* a drop and a def.
//
// Treating it as a *drop* ensures that we consider the local to be
// "drop live" here, which keeps alive any data that the `Drop` impl
// could access.
//
// Treating it as a *def* ensures that we don't create an unnecessarily large "use live"
// set - we'll stop tracing backwards from a use when we hit this def.
Some(DefUse::DropAndDef) => {
self.insert_drop(local, location);
self.insert_def(local, location);
}
None => {}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>

PlaceContext::MutatingUse(
MutatingUseContext::Store
| MutatingUseContext::DropAndReplace
| MutatingUseContext::Deinit
| MutatingUseContext::SetDiscriminant
| MutatingUseContext::AsmOutput
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ macro_rules! make_mir_visitor {
} => {
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Drop),
PlaceContext::MutatingUse(MutatingUseContext::DropAndReplace),
location
);
self.visit_operand(value, location);
Expand Down Expand Up @@ -1267,6 +1267,8 @@ pub enum MutatingUseContext {
Yield,
/// Being dropped.
Drop,
/// A `DropAndReplace` terminator
DropAndReplace,
/// Mutable borrow.
Borrow,
/// AddressOf for *mut pointer.
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_mir_dataflow/src/impls/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ impl DefUse {
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
unreachable!("A projection could be a def or a use and must be handled separately")
}

PlaceContext::MutatingUse(MutatingUseContext::DropAndReplace) => {
unreachable!(
"DropAndReplace terminators should have been removed by drop elaboration: place {:?}",
place
)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_transform/src/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,7 @@ impl Visitor<'_> for CanConstProp {
// whether they'd be fine right now.
MutatingUse(MutatingUseContext::Yield)
| MutatingUse(MutatingUseContext::Drop)
| MutatingUse(MutatingUseContext::DropAndReplace)
| MutatingUse(MutatingUseContext::Retag)
// These can't ever be propagated under any scheme, as we can't reason about indirect
// mutation.
Expand Down
24 changes: 24 additions & 0 deletions src/test/ui/borrowck/drop-in-loop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// A version of `issue-70919-drop-in-loop`, but without
// the necessary `drop` call.
//
// This should fail to compile, since the `Drop` impl
// for `WrapperWithDrop` could observe the changed
// `base` value.

struct WrapperWithDrop<'a>(&'a mut bool);
impl<'a> Drop for WrapperWithDrop<'a> {
fn drop(&mut self) {
}
}

fn drop_in_loop() {
let mut base = true;
let mut wrapper = WrapperWithDrop(&mut base);
loop {
base = false; //~ ERROR: cannot assign to `base`
wrapper = WrapperWithDrop(&mut base);
}
}

fn main() {
}
14 changes: 14 additions & 0 deletions src/test/ui/borrowck/drop-in-loop.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0506]: cannot assign to `base` because it is borrowed
--> $DIR/drop-in-loop.rs:18:9
|
LL | let mut wrapper = WrapperWithDrop(&mut base);
| --------- borrow of `base` occurs here
LL | loop {
LL | base = false;
| ^^^^^^^^^^^^ assignment to borrowed `base` occurs here
LL | wrapper = WrapperWithDrop(&mut base);
| ------- borrow might be used here, when `wrapper` is dropped and runs the `Drop` code for type `WrapperWithDrop`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0506`.
25 changes: 25 additions & 0 deletions src/test/ui/borrowck/issue-70919-drop-in-loop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Regression test for issue #70919
// Tests that we don't emit a spurious "borrow might be used" error
// when we have an explicit `drop` in a loop

// check-pass

struct WrapperWithDrop<'a>(&'a mut bool);
impl<'a> Drop for WrapperWithDrop<'a> {
fn drop(&mut self) {
}
}

fn drop_in_loop() {
let mut base = true;
let mut wrapper = WrapperWithDrop(&mut base);
loop {
drop(wrapper);

base = false;
wrapper = WrapperWithDrop(&mut base);
}
}

fn main() {
}