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

Point out implicit deref coercions in borrow #81629

Merged
merged 4 commits into from
Feb 23, 2021
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
41 changes: 37 additions & 4 deletions compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ use rustc_index::vec::Idx;
use rustc_middle::mir::{
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
};
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
use rustc_span::source_map::DesugaringKind;
use rustc_span::Span;
use rustc_middle::ty::{self, suggest_constraining_type_param, Instance, Ty};
use rustc_span::{source_map::DesugaringKind, symbol::sym, Span};

use crate::dataflow::drop_flag_effects;
use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex};
Expand Down Expand Up @@ -1543,9 +1542,43 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
None,
);

self.explain_deref_coercion(loan, &mut err);

err.buffer(&mut self.errors_buffer);
}

fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
let tcx = self.infcx.tcx;
if let (
Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
Some((method_did, method_substs)),
) = (
&self.body[loan.reserve_location.block].terminator,
crate::util::find_self_call(
tcx,
self.body,
loan.assigned_place.local,
loan.reserve_location.block,
),
) {
if tcx.is_diagnostic_item(sym::deref_method, method_did) {
let deref_target =
tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
Instance::resolve(tcx, self.param_env, deref_target, method_substs)
.transpose()
});
if let Some(Ok(instance)) = deref_target {
let deref_target_ty = instance.ty(tcx, self.param_env);
err.note(&format!(
"borrow occurs due to deref coercion to `{}`",
deref_target_ty
));
err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
}
}
}
}

/// Reports an illegal reassignment; for example, an assignment to
/// (part of) a non-`mut` local that occurs potentially after that
/// local has already been initialized. `place` is the path being
Expand Down
26 changes: 26 additions & 0 deletions src/test/ui/borrowck/issue-81365-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::ops::Deref;

struct DerefTarget {
target_field: bool,
}
struct Container {
target: DerefTarget,
container_field: bool,
}

impl Deref for Container {
type Target = DerefTarget;
fn deref(&self) -> &Self::Target {
&self.target
}
}

impl Container {
fn bad_borrow(&mut self) {
let first = &self.target_field;
self.container_field = true; //~ ERROR E0506
first;
}
}
Comment on lines +1 to +24
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason the different tests couldn't be merged into fewer files?

Copy link
Contributor Author

@sledgehammervampire sledgehammervampire Feb 22, 2021

Choose a reason for hiding this comment

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

Well, I don't understand the previous phases that transformed the code into MIR, so I made some tests with the intent of something like "what happens to double implicit derefs?", "what happens to indexing and function calls?", "what happens with implicitly inserted v. explicitly inserted derefs?". The only reasons I haven't merged them is because I don't know enough to decide what distinctions in the tests are important, and also that I'm not sure if any existing tests cover what I've already written. If you think that some tests are redundant, I will remove them.


fn main() {}
20 changes: 20 additions & 0 deletions src/test/ui/borrowck/issue-81365-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0506]: cannot assign to `self.container_field` because it is borrowed
--> $DIR/issue-81365-1.rs:21:9
|
LL | let first = &self.target_field;
| ---- borrow of `self.container_field` occurs here
LL | self.container_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
LL | first;
| ----- borrow later used here
|
= note: borrow occurs due to deref coercion to `DerefTarget`
note: deref defined here
--> $DIR/issue-81365-1.rs:12:5
|
LL | type Target = DerefTarget;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0506`.
26 changes: 26 additions & 0 deletions src/test/ui/borrowck/issue-81365-10.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::ops::Deref;

struct DerefTarget {
target_field: bool,
}
struct Container {
target: DerefTarget,
container_field: bool,
}

impl Deref for Container {
type Target = DerefTarget;
fn deref(&self) -> &Self::Target {
&self.target
}
}

impl Container {
fn bad_borrow(&mut self) {
let first = &self.deref().target_field;
self.container_field = true; //~ ERROR E0506
first;
}
}

fn main() {}
13 changes: 13 additions & 0 deletions src/test/ui/borrowck/issue-81365-10.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0506]: cannot assign to `self.container_field` because it is borrowed
--> $DIR/issue-81365-10.rs:21:9
|
LL | let first = &self.deref().target_field;
| ---- borrow of `self.container_field` occurs here
LL | self.container_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
LL | first;
| ----- borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0506`.
32 changes: 32 additions & 0 deletions src/test/ui/borrowck/issue-81365-11.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::ops::{Deref, DerefMut};

struct DerefTarget {
target_field: bool,
}
struct Container {
target: DerefTarget,
container_field: bool,
}

impl Deref for Container {
type Target = DerefTarget;
fn deref(&self) -> &Self::Target {
&self.target
}
}

impl DerefMut for Container {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.target
}
}

impl Container {
fn bad_borrow(&mut self) {
let first = &mut self.target_field;
self.container_field = true; //~ ERROR E0506
first;
}
}

fn main() {}
13 changes: 13 additions & 0 deletions src/test/ui/borrowck/issue-81365-11.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0506]: cannot assign to `self.container_field` because it is borrowed
--> $DIR/issue-81365-11.rs:27:9
|
LL | let first = &mut self.target_field;
| ---- borrow of `self.container_field` occurs here
LL | self.container_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
LL | first;
| ----- borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0506`.
30 changes: 30 additions & 0 deletions src/test/ui/borrowck/issue-81365-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use std::ops::Deref;

struct DerefTarget {
target_field: bool,
}
struct Container {
target: DerefTarget,
container_field: bool,
}

impl Deref for Container {
type Target = DerefTarget;
fn deref(&self) -> &Self::Target {
&self.target
}
}

struct Outer {
container: Container,
}

impl Outer {
fn bad_borrow(&mut self) {
let first = &self.container.target_field;
self.container.container_field = true; //~ ERROR E0506
first;
}
}

fn main() {}
20 changes: 20 additions & 0 deletions src/test/ui/borrowck/issue-81365-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0506]: cannot assign to `self.container.container_field` because it is borrowed
--> $DIR/issue-81365-2.rs:25:9
|
LL | let first = &self.container.target_field;
| -------------- borrow of `self.container.container_field` occurs here
LL | self.container.container_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
LL | first;
| ----- borrow later used here
|
= note: borrow occurs due to deref coercion to `DerefTarget`
note: deref defined here
--> $DIR/issue-81365-2.rs:12:5
|
LL | type Target = DerefTarget;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0506`.
37 changes: 37 additions & 0 deletions src/test/ui/borrowck/issue-81365-3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::ops::Deref;

struct DerefTarget {
target_field: bool,
}
struct Container {
target: DerefTarget,
container_field: bool,
}

impl Deref for Container {
type Target = DerefTarget;
fn deref(&self) -> &Self::Target {
&self.target
}
}

struct Outer {
container: Container,
}

impl Deref for Outer {
type Target = Container;
fn deref(&self) -> &Self::Target {
&self.container
}
}

impl Outer {
fn bad_borrow(&mut self) {
let first = &self.target_field;
self.container.container_field = true; //~ ERROR E0506
first;
}
}

fn main() {}
20 changes: 20 additions & 0 deletions src/test/ui/borrowck/issue-81365-3.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0506]: cannot assign to `self.container.container_field` because it is borrowed
--> $DIR/issue-81365-3.rs:32:9
|
LL | let first = &self.target_field;
| ---- borrow of `self.container.container_field` occurs here
LL | self.container.container_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
LL | first;
| ----- borrow later used here
|
= note: borrow occurs due to deref coercion to `Container`
note: deref defined here
--> $DIR/issue-81365-3.rs:23:5
|
LL | type Target = Container;
| ^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0506`.
38 changes: 38 additions & 0 deletions src/test/ui/borrowck/issue-81365-4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::ops::Deref;

struct DerefTarget {
target_field: bool,
}
struct Container {
target: DerefTarget,
container_field: bool,
}

impl Deref for Container {
type Target = DerefTarget;
fn deref(&self) -> &Self::Target {
&self.target
}
}

struct Outer {
container: Container,
outer_field: bool,
}

impl Deref for Outer {
type Target = Container;
fn deref(&self) -> &Self::Target {
&self.container
}
}

impl Outer {
fn bad_borrow(&mut self) {
let first = &self.target_field;
self.outer_field = true; //~ ERROR E0506
first;
}
}

fn main() {}
20 changes: 20 additions & 0 deletions src/test/ui/borrowck/issue-81365-4.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0506]: cannot assign to `self.outer_field` because it is borrowed
--> $DIR/issue-81365-4.rs:33:9
|
LL | let first = &self.target_field;
| ---- borrow of `self.outer_field` occurs here
LL | self.outer_field = true;
| ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.outer_field` occurs here
LL | first;
| ----- borrow later used here
|
= note: borrow occurs due to deref coercion to `Container`
note: deref defined here
--> $DIR/issue-81365-4.rs:24:5
|
LL | type Target = Container;
| ^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0506`.
33 changes: 33 additions & 0 deletions src/test/ui/borrowck/issue-81365-5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::ops::Deref;

struct DerefTarget {
target_field: bool,
}

impl DerefTarget {
fn get(&self) -> &bool {
&self.target_field
}
}

struct Container {
target: DerefTarget,
container_field: bool,
}

impl Deref for Container {
type Target = DerefTarget;
fn deref(&self) -> &Self::Target {
&self.target
}
}

impl Container {
fn bad_borrow(&mut self) {
let first = self.get();
self.container_field = true; //~ ERROR E0506
first;
}
}

fn main() {}
Loading