Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Test cases for handling mutable references
  • Loading branch information
arora-aman committed Jan 29, 2021
commit 1373f988fa662aa43a22a9b13300aabe9ce2213b
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Test that if we deref an immutable borrow to access a Place,
// then we can't mutate the final place.

#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete

fn main() {
let mut x = (format!(""), format!("X2"));
let mut y = (&x, "Y");
let z = (&mut y, "Z");

// `x.0` is mutable but we access `x` via `z.0.0`, which is an immutable reference and
// therefore can't be mutated.
let mut c = || {
//~^ ERROR: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
z.0.0.0 = format!("X1");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would have expected us to not try read the place z on its own. When the feature isn't enabled we only read z.0.0.0.

Debug logs from the report_mutability_error when the feature is enabled.

DEBUG rustc_mir::borrow_check::diagnostics::mutability_errors report_mutability_error: access_place_desc=Some("z")
....
DEBUG rustc_mir::borrow_check::diagnostics::mutability_errors report_mutability_error: access_place_desc=Some("z.0.0.0")

Copy link
Contributor

Choose a reason for hiding this comment

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

This is definitely not the error message we want-- we'd like the error to be reported on z.0.0.0, right? I think this may be related to my comment about the used_mut_upvars but I'm not 100% sure.

//~^ ERROR: cannot assign to `z`, as it is not declared as mutable
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I hack around this error in a later commit

};

c();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/cant-mutate-imm-borrow.rs:4:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error[E0594]: cannot assign to `z`, as it is not declared as mutable
--> $DIR/cant-mutate-imm-borrow.rs:16:9
|
LL | let z = (&mut y, "Z");
| - help: consider changing this to be mutable: `mut z`
...
LL | z.0.0.0 = format!("X1");
| ^^^^^^^ cannot assign

error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
--> $DIR/cant-mutate-imm-borrow.rs:14:17
|
LL | let mut c = || {
| ^^ cannot borrow as mutable
LL |
LL | z.0.0.0 = format!("X1");
| - mutable borrow occurs due to use of `z.0.0.0` in closure

error: aborting due to 2 previous errors; 1 warning emitted

Some errors have detailed explanations: E0594, E0596.
For more information about an error, try `rustc --explain E0594`.
40 changes: 40 additions & 0 deletions src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Test that we can't mutate a place if we need to deref an imm-borrow
// to reach it.

#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete

fn imm_mut_ref() {
let mut x = String::new();
let y = String::new();
let mref_x = &mut x;
let ref_mref_x = &mref_x;

let c = || {
//~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
**ref_mref_x = y;
//~^ERROR: cannot assign to `ref_mref_x`, as it is not declared as mutable
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This gets hacked away as well in a later commit.

};

c();
}

fn mut_imm_ref() {
let x = String::new();
let y = String::new();
let mut ref_x = &x;
let mref_ref_x = &mut ref_x;

let c = || {
//~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
**mref_ref_x = y;
//~^ERROR: cannot assign to `mref_ref_x`, as it is not declared as mutable
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This gets hacked away as well in a later commit.

};

c();
}

fn main() {
imm_mut_ref();
mut_imm_ref();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/mut_ref.rs:4:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error[E0594]: cannot assign to `ref_mref_x`, as it is not declared as mutable
--> $DIR/mut_ref.rs:15:9
|
LL | let ref_mref_x = &mref_x;
| ---------- help: consider changing this to be mutable: `mut ref_mref_x`
...
LL | **ref_mref_x = y;
| ^^^^^^^^^^^^ cannot assign

error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
--> $DIR/mut_ref.rs:13:13
|
LL | let ref_mref_x = &mref_x;
| ------- help: consider changing this to be a mutable reference: `&mut mref_x`
LL |
LL | let c = || {
| ^^ `ref_mref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
LL |
LL | **ref_mref_x = y;
| ---------- mutable borrow occurs due to use of `**ref_mref_x` in closure

error[E0594]: cannot assign to `mref_ref_x`, as it is not declared as mutable
--> $DIR/mut_ref.rs:30:9
|
LL | let mref_ref_x = &mut ref_x;
| ---------- help: consider changing this to be mutable: `mut mref_ref_x`
...
LL | **mref_ref_x = y;
| ^^^^^^^^^^^^ cannot assign

error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
--> $DIR/mut_ref.rs:28:13
|
LL | let c = || {
| ^^ cannot borrow as mutable
LL |
LL | **mref_ref_x = y;
| ---------- mutable borrow occurs due to use of `**mref_ref_x` in closure

error: aborting due to 4 previous errors; 1 warning emitted

Some errors have detailed explanations: E0594, E0596.
For more information about an error, try `rustc --explain E0594`.
56 changes: 56 additions & 0 deletions src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// run-pass

// Test that we can mutate a place through a mut-borrow
// that is captured by the closure

#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete

// Check that we can mutate when one deref is required
fn mut_ref_1() {
let mut x = String::new();
let rx = &mut x;

let mut c = || {
*rx = String::new();
};

c();
}

// Similar example as mut_ref_1, we don't deref the imm-borrow here,
// and so we are allowed to mutate.
fn mut_ref_2() {
let x = String::new();
let y = String::new();
let mut ref_x = &x;
let m_ref_x = &mut ref_x;

let mut c = || {
*m_ref_x = &y;
};

c();
}

// Check that we can mutate when multiple derefs of mut-borrows are required to reach
// the target place.
// It works because all derefs are mutable, if either of them was an immutable
// borrow, then we would not be able to deref.
fn mut_mut_ref() {
let mut x = String::new();
let mut mref_x = &mut x;
let m_mref_x = &mut mref_x;

let mut c = || {
**m_mref_x = String::new();
};

c();
}

fn main() {
mut_ref_1();
mut_ref_2();
mut_mut_ref();
}
11 changes: 11 additions & 0 deletions src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/mut_ref.rs:6:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

warning: 1 warning emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// run-pass

// Test that we can mutate a place through a mut-borrow
// that is captured by the closure
//
// More specifically we test that the if the mutable reference isn't root variable of a capture
// but rather accessed while acessing the precise capture.

#![feature(capture_disjoint_fields)]
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete

fn main() {
let mut t = (10, 10);

let t1 = (&mut t, 10);

let mut c = || {
// Mutable because (*t.0) is mutable
t1.0.0 += 10;
};

c();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/mut_ref_struct_mem.rs:9:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

warning: 1 warning emitted