diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index cd16a88e5fc3c..b0b58a8d00367 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -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}; @@ -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 diff --git a/src/test/ui/borrowck/issue-81365-1.rs b/src/test/ui/borrowck/issue-81365-1.rs new file mode 100644 index 0000000000000..8e212a7701647 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-1.rs @@ -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; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-1.stderr b/src/test/ui/borrowck/issue-81365-1.stderr new file mode 100644 index 0000000000000..ef88d7f14a39b --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-1.stderr @@ -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`. diff --git a/src/test/ui/borrowck/issue-81365-10.rs b/src/test/ui/borrowck/issue-81365-10.rs new file mode 100644 index 0000000000000..7602e184a9f9d --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-10.rs @@ -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() {} diff --git a/src/test/ui/borrowck/issue-81365-10.stderr b/src/test/ui/borrowck/issue-81365-10.stderr new file mode 100644 index 0000000000000..891f70ed7f6b4 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-10.stderr @@ -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`. diff --git a/src/test/ui/borrowck/issue-81365-11.rs b/src/test/ui/borrowck/issue-81365-11.rs new file mode 100644 index 0000000000000..6b558c65d3d61 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-11.rs @@ -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() {} diff --git a/src/test/ui/borrowck/issue-81365-11.stderr b/src/test/ui/borrowck/issue-81365-11.stderr new file mode 100644 index 0000000000000..0770c136632db --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-11.stderr @@ -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`. diff --git a/src/test/ui/borrowck/issue-81365-2.rs b/src/test/ui/borrowck/issue-81365-2.rs new file mode 100644 index 0000000000000..fbbdd93b97cc8 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-2.rs @@ -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() {} diff --git a/src/test/ui/borrowck/issue-81365-2.stderr b/src/test/ui/borrowck/issue-81365-2.stderr new file mode 100644 index 0000000000000..e71edb509649f --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-2.stderr @@ -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`. diff --git a/src/test/ui/borrowck/issue-81365-3.rs b/src/test/ui/borrowck/issue-81365-3.rs new file mode 100644 index 0000000000000..9a9e3a3135822 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-3.rs @@ -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() {} diff --git a/src/test/ui/borrowck/issue-81365-3.stderr b/src/test/ui/borrowck/issue-81365-3.stderr new file mode 100644 index 0000000000000..70bb6bb93a94b --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-3.stderr @@ -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`. diff --git a/src/test/ui/borrowck/issue-81365-4.rs b/src/test/ui/borrowck/issue-81365-4.rs new file mode 100644 index 0000000000000..b2643eb3358ac --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-4.rs @@ -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() {} diff --git a/src/test/ui/borrowck/issue-81365-4.stderr b/src/test/ui/borrowck/issue-81365-4.stderr new file mode 100644 index 0000000000000..e714bb86d1cd2 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-4.stderr @@ -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`. diff --git a/src/test/ui/borrowck/issue-81365-5.rs b/src/test/ui/borrowck/issue-81365-5.rs new file mode 100644 index 0000000000000..d36b79615e3ad --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-5.rs @@ -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() {} diff --git a/src/test/ui/borrowck/issue-81365-5.stderr b/src/test/ui/borrowck/issue-81365-5.stderr new file mode 100644 index 0000000000000..7c0e9f43bd0b0 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-5.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-5.rs:28:9 + | +LL | let first = self.get(); + | ---- 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-5.rs:19:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-6.rs b/src/test/ui/borrowck/issue-81365-6.rs new file mode 100644 index 0000000000000..85ea77756b3ba --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-6.rs @@ -0,0 +1,23 @@ +use std::ops::Deref; + +struct Container { + target: Vec<()>, + container_field: bool, +} + +impl Deref for Container { + type Target = [()]; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = &self[0]; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-6.stderr b/src/test/ui/borrowck/issue-81365-6.stderr new file mode 100644 index 0000000000000..85ed6acca3d41 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-6.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-6.rs:18:9 + | +LL | let first = &self[0]; + | ---- 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 `[()]` +note: deref defined here + --> $DIR/issue-81365-6.rs:9:5 + | +LL | type Target = [()]; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-7.rs b/src/test/ui/borrowck/issue-81365-7.rs new file mode 100644 index 0000000000000..cbf70f11a9acf --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-7.rs @@ -0,0 +1,24 @@ +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 + } +} + +fn bad_borrow(c: &mut Container) { + let first = &c.target_field; + c.container_field = true; //~ ERROR E0506 + first; +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-7.stderr b/src/test/ui/borrowck/issue-81365-7.stderr new file mode 100644 index 0000000000000..506732ec0c5b4 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-7.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `c.container_field` because it is borrowed + --> $DIR/issue-81365-7.rs:20:5 + | +LL | let first = &c.target_field; + | - borrow of `c.container_field` occurs here +LL | c.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `c.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-7.rs:12:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-8.rs b/src/test/ui/borrowck/issue-81365-8.rs new file mode 100644 index 0000000000000..0bb1033fb42f7 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-8.rs @@ -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; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-8.stderr b/src/test/ui/borrowck/issue-81365-8.stderr new file mode 100644 index 0000000000000..716b6e9b51fd0 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-8.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-8.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-8.rs:12:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-9.rs b/src/test/ui/borrowck/issue-81365-9.rs new file mode 100644 index 0000000000000..cd57afa288df7 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-9.rs @@ -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 = &Deref::deref(self).target_field; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-9.stderr b/src/test/ui/borrowck/issue-81365-9.stderr new file mode 100644 index 0000000000000..c7d48214fd4a8 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-9.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-9.rs:21:9 + | +LL | let first = &Deref::deref(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`.