Skip to content

[NLL] Fix various unused mut errors #51964

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

Merged
merged 1 commit into from
Jul 5, 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
55 changes: 33 additions & 22 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,26 +1181,37 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
// We need to report back the list of mutable upvars that were
// moved into the closure and subsequently used by the closure,
// in order to populate our used_mut set.
if let AggregateKind::Closure(def_id, _) = &**aggregate_kind {
let BorrowCheckResult {
used_mut_upvars, ..
} = self.tcx.mir_borrowck(*def_id);
debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
for field in used_mut_upvars {
match operands[field.index()] {
Operand::Move(Place::Local(local)) => {
self.used_mut.insert(local);
}
Operand::Move(ref place @ Place::Projection(_)) => {
if let Some(field) = self.is_upvar_field_projection(place) {
self.used_mut_upvars.push(field);
match **aggregate_kind {
AggregateKind::Closure(def_id, _)
| AggregateKind::Generator(def_id, _, _) => {
let BorrowCheckResult {
used_mut_upvars, ..
} = self.tcx.mir_borrowck(def_id);
debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
for field in used_mut_upvars {
// This relies on the current way that by-value
// captures of a closure are copied/moved directly
// when generating MIR.
match operands[field.index()] {
Operand::Move(Place::Local(local))
| Operand::Copy(Place::Local(local)) => {
self.used_mut.insert(local);
}
Operand::Move(ref place @ Place::Projection(_))
| Operand::Copy(ref place @ Place::Projection(_)) => {
if let Some(field) = self.is_upvar_field_projection(place) {
self.used_mut_upvars.push(field);
}
}
Operand::Move(Place::Static(..))
| Operand::Copy(Place::Static(..))
| Operand::Constant(..) => {}
}
Operand::Move(Place::Static(..))
| Operand::Copy(..)
| Operand::Constant(..) => {}
}
}
AggregateKind::Adt(..)
| AggregateKind::Array(..)
| AggregateKind::Tuple { .. } => (),
}

for operand in operands {
Expand Down Expand Up @@ -1939,6 +1950,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
}
}
}
RootPlace {
place: _,
is_local_mutation_allowed: LocalMutationIsAllowed::Yes,
} => {}
RootPlace {
place: place @ Place::Projection(_),
is_local_mutation_allowed: _,
Expand Down Expand Up @@ -2114,13 +2129,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
match *place {
Place::Projection(ref proj) => match proj.elem {
ProjectionElem::Field(field, _ty) => {
let is_projection_from_ty_closure = proj
.base
.ty(self.mir, self.tcx)
.to_ty(self.tcx)
.is_closure();
let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);

if is_projection_from_ty_closure {
if base_ty.is_closure() || base_ty.is_generator() {
Some(field)
} else {
None
Expand Down
27 changes: 23 additions & 4 deletions src/librustc_mir/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,29 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
ExprKind::Closure { closure_id, substs, upvars, movability } => {
// see (*) above
let mut operands: Vec<_> =
upvars.into_iter()
.map(|upvar| unpack!(block = this.as_operand(block, scope, upvar)))
.collect();
let mut operands: Vec<_> = upvars
.into_iter()
.map(|upvar| {
let upvar = this.hir.mirror(upvar);
match Category::of(&upvar.kind) {
// Use as_place to avoid creating a temporary when
// moving a variable into a closure, so that
// borrowck knows which variables to mark as being
// used as mut. This is OK here because the upvar
// expressions have no side effects and act on
// disjoint places.
// This occurs when capturing by copy/move, while
// by reference captures use as_operand
Some(Category::Place) => {
let place = unpack!(block = this.as_place(block, upvar));
this.consume_by_copy_or_move(place)
}
_ => {
unpack!(block = this.as_operand(block, scope, upvar))
}
}
})
.collect();
let result = match substs {
UpvarSubsts::Generator(substs) => {
let movability = movability.unwrap();
Expand Down
13 changes: 10 additions & 3 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,11 +672,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
{
// Allocate locals for the function arguments
for &ArgInfo(ty, _, pattern, _) in arguments.iter() {
// If this is a simple binding pattern, give the local a nice name for debuginfo.
// If this is a simple binding pattern, give the local a name for
// debuginfo and so that error reporting knows that this is a user
// variable. For any other pattern the pattern introduces new
// variables which will be named instead.
let mut name = None;
if let Some(pat) = pattern {
if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
name = Some(ident.name);
match pat.node {
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, _)
| hir::PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, _) => {
name = Some(ident.name);
}
_ => (),
}
}

Expand Down
21 changes: 7 additions & 14 deletions src/test/mir-opt/end_region_7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,31 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
// ...
// let mut _2: ();
// let mut _3: [closure@NodeId(22) d:D];
// let mut _4: D;
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_3);
// StorageLive(_4);
// _4 = move _1;
// _3 = [closure@NodeId(22)] { d: move _4 };
// drop(_4) -> [return: bb4, unwind: bb3];
// _3 = [closure@NodeId(22)] { d: move _1 };
// _2 = const foo(move _3) -> [return: bb2, unwind: bb4];
// }
// bb1: {
// resume;
// }
// bb2: {
// drop(_1) -> bb1;
// drop(_3) -> [return: bb5, unwind: bb3];
// }
// bb3: {
// drop(_3) -> bb2;
// drop(_1) -> bb1;
// }
// bb4: {
// StorageDead(_4);
// _2 = const foo(move _3) -> [return: bb5, unwind: bb3];
// drop(_3) -> bb3;
// }
// bb5: {
// drop(_3) -> [return: bb6, unwind: bb2];
// }
// bb6: {
// StorageDead(_3);
// _0 = ();
// drop(_1) -> [return: bb7, unwind: bb1];
// drop(_1) -> [return: bb6, unwind: bb1];
// }
// bb7: {
// bb6: {
// StorageDead(_1);
// return;
// }
Expand Down
6 changes: 1 addition & 5 deletions src/test/mir-opt/end_region_8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,13 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
// ...
// let mut _3: ();
// let mut _4: [closure@NodeId(22) r:&'19s D];
// let mut _5: &'21_1rs D;
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_2);
// _2 = &'21_1rs _1;
// StorageLive(_4);
// StorageLive(_5);
// _5 = _2;
// _4 = [closure@NodeId(22)] { r: move _5 };
// StorageDead(_5);
// _4 = [closure@NodeId(22)] { r: _2 };
// _3 = const foo(move _4) -> [return: bb2, unwind: bb3];
// }
// bb1: {
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/nll/capture-mut-ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2018 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 capturing a mutable reference by move and assigning to its
// referent doesn't make the unused mut lint think that it is mutable.

#![feature(nll)]
#![deny(unused_mut)]

fn mutable_upvar() {
let mut x = &mut 0;
//~^ ERROR
move || {
*x = 1;
};
}

fn main() {}
16 changes: 16 additions & 0 deletions src/test/ui/nll/capture-mut-ref.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: variable does not need to be mutable
--> $DIR/capture-mut-ref.rs:18:9
|
LL | let mut x = &mut 0;
| ----^
| |
| help: remove this `mut`
|
note: lint level defined here
--> $DIR/capture-mut-ref.rs:15:9
|
LL | #![deny(unused_mut)]
| ^^^^^^^^^^

error: aborting due to previous error

64 changes: 64 additions & 0 deletions src/test/ui/nll/extra-unused-mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2018 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.

// extra unused mut lint tests for #51918

// run-pass

#![feature(generators, nll)]
#![deny(unused_mut)]

fn ref_argument(ref _y: i32) {}

// #51801
fn mutable_upvar() {
let mut x = 0;
move || {
x = 1;
};
}

// #50897
fn generator_mutable_upvar() {
let mut x = 0;
move || {
x = 1;
yield;
};
}

// #51830
fn ref_closure_argument() {
let _ = Some(0).as_ref().map(|ref _a| true);
}

struct Expr {
attrs: Vec<u32>,
}

// #51904
fn parse_dot_or_call_expr_with(mut attrs: Vec<u32>) {
let x = Expr { attrs: vec![] };
Some(Some(x)).map(|expr|
expr.map(|mut expr| {
attrs.push(666);
expr.attrs = attrs;
expr
})
);
}

fn main() {
ref_argument(0);
mutable_upvar();
generator_mutable_upvar();
ref_closure_argument();
parse_dot_or_call_expr_with(Vec::new());
}
24 changes: 24 additions & 0 deletions src/test/ui/nll/generator-upvar-mutability.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2018 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 generators respect the muatability of their upvars.

#![feature(generators, nll)]

fn mutate_upvar() {
let x = 0;
move || {
x = 1;
//~^ ERROR
yield;
};
}

fn main() {}
9 changes: 9 additions & 0 deletions src/test/ui/nll/generator-upvar-mutability.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0594]: cannot assign to immutable item `x`
--> $DIR/generator-upvar-mutability.rs:18:9
|
LL | x = 1;
| ^^^^^ cannot assign

error: aborting due to previous error

For more information about this error, try `rustc --explain E0594`.