From 50728e52455804143e5cc601004381e395a1e2f7 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 26 Mar 2017 19:35:46 +0300 Subject: [PATCH 1/2] borrowck: consolidate `mut` suggestions This converts all of borrowck's `mut` suggestions to a new `mc::ImmutabilityBlame` API instead of the current mix of various hacks. Fixes #35937. Fixes #40823. --- src/librustc/middle/mem_categorization.rs | 140 +++++------- .../borrowck/gather_loans/mod.rs | 9 - src/librustc_borrowck/borrowck/mod.rs | 208 ++++++++++-------- src/librustc_borrowck/diagnostics.rs | 6 +- .../compile-fail/augmented-assignments.rs | 2 +- .../borrowck/borrowck-issue-14498.rs | 6 +- src/test/compile-fail/issue-33819.rs | 2 +- src/test/compile-fail/mut-suggestion.rs | 4 +- .../huge_multispan_highlight.stderr | 2 +- src/test/ui/did_you_mean/issue-31424.stderr | 2 + src/test/ui/did_you_mean/issue-35937.rs | 31 +++ src/test/ui/did_you_mean/issue-35937.stderr | 26 +++ src/test/ui/did_you_mean/issue-38147-2.stderr | 2 +- src/test/ui/did_you_mean/issue-38147-3.stderr | 4 +- src/test/ui/did_you_mean/issue-39544.rs | 11 +- src/test/ui/did_you_mean/issue-39544.stderr | 19 +- src/test/ui/did_you_mean/issue-40823.rs | 14 ++ src/test/ui/did_you_mean/issue-40823.stderr | 8 + ...ck-borrow-overloaded-auto-deref-mut.stderr | 8 +- ...orrowck-borrow-overloaded-deref-mut.stderr | 4 +- .../ui/span/borrowck-object-mutability.stderr | 3 + 21 files changed, 304 insertions(+), 207 deletions(-) create mode 100644 src/test/ui/did_you_mean/issue-35937.rs create mode 100644 src/test/ui/did_you_mean/issue-35937.stderr create mode 100644 src/test/ui/did_you_mean/issue-40823.rs create mode 100644 src/test/ui/did_you_mean/issue-40823.stderr diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 0cf53826dd427..3b52e85e08e32 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -194,76 +194,75 @@ pub struct cmt_<'tcx> { pub type cmt<'tcx> = Rc>; +pub enum ImmutabilityBlame<'tcx> { + ImmLocal(ast::NodeId), + ClosureEnv(ast::NodeId), + LocalDeref(ast::NodeId), + AdtFieldDeref(&'tcx ty::AdtDef, &'tcx ty::FieldDef) +} + impl<'tcx> cmt_<'tcx> { - pub fn get_def(&self) -> Option { - match self.cat { - Categorization::Deref(ref cmt, ..) | - Categorization::Interior(ref cmt, _) | - Categorization::Downcast(ref cmt, _) => { - if let Categorization::Local(nid) = cmt.cat { - Some(nid) - } else { - None - } + fn resolve_field(&self, field_name: FieldName) -> (&'tcx ty::AdtDef, &'tcx ty::FieldDef) + { + let adt_def = self.ty.ty_adt_def().unwrap_or_else(|| { + bug!("interior cmt {:?} is not an ADT", self) + }); + let variant_def = match self.cat { + Categorization::Downcast(_, variant_did) => { + adt_def.variant_with_id(variant_did) } - _ => None - } + _ => { + assert!(adt_def.is_univariant()); + &adt_def.variants[0] + } + }; + let field_def = match field_name { + NamedField(name) => variant_def.field_named(name), + PositionalField(idx) => &variant_def.fields[idx] + }; + (adt_def, field_def) } - pub fn get_field(&self, name: ast::Name) -> Option { + pub fn immutability_blame(&self) -> Option> { match self.cat { - Categorization::Deref(ref cmt, ..) | - Categorization::Interior(ref cmt, _) | - Categorization::Downcast(ref cmt, _) => { - if let Categorization::Local(_) = cmt.cat { - if let ty::TyAdt(def, _) = self.ty.sty { - if def.is_struct() { - return def.struct_variant().find_field_named(name).map(|x| x.did); + Categorization::Deref(ref base_cmt, _, BorrowedPtr(ty::ImmBorrow, _)) | + Categorization::Deref(ref base_cmt, _, Implicit(ty::ImmBorrow, _)) => { + // try to figure out where the immutable reference came from + match base_cmt.cat { + Categorization::Local(node_id) => + Some(ImmutabilityBlame::LocalDeref(node_id)), + Categorization::Interior(ref base_cmt, InteriorField(field_name)) => { + let (adt_def, field_def) = base_cmt.resolve_field(field_name); + Some(ImmutabilityBlame::AdtFieldDeref(adt_def, field_def)) + } + Categorization::Upvar(Upvar { id, .. }) => { + if let NoteClosureEnv(..) = self.note { + Some(ImmutabilityBlame::ClosureEnv(id.closure_expr_id)) + } else { + None } } - None - } else { - cmt.get_field(name) + _ => None } } - _ => None - } - } - - pub fn get_field_name(&self) -> Option { - match self.cat { - Categorization::Interior(_, ref ik) => { - if let InteriorKind::InteriorField(FieldName::NamedField(name)) = *ik { - Some(name) - } else { - None - } + Categorization::Local(node_id) => { + Some(ImmutabilityBlame::ImmLocal(node_id)) } - Categorization::Deref(ref cmt, ..) | - Categorization::Downcast(ref cmt, _) => { - cmt.get_field_name() + Categorization::Rvalue(..) | + Categorization::Upvar(..) | + Categorization::Deref(.., UnsafePtr(..)) => { + // This should not be reachable up to inference limitations. + None } - _ => None, - } - } - - pub fn get_arg_if_immutable(&self, map: &hir_map::Map) -> Option { - match self.cat { - Categorization::Deref(ref cmt, ..) | - Categorization::Interior(ref cmt, _) | - Categorization::Downcast(ref cmt, _) => { - if let Categorization::Local(nid) = cmt.cat { - if let ty::TyAdt(_, _) = self.ty.sty { - if let ty::TyRef(_, ty::TypeAndMut{mutbl: MutImmutable, ..}) = cmt.ty.sty { - return Some(nid); - } - } - None - } else { - cmt.get_arg_if_immutable(map) - } + Categorization::Interior(ref base_cmt, _) | + Categorization::Downcast(ref base_cmt, _) | + Categorization::Deref(ref base_cmt, _, _) => { + base_cmt.immutability_blame() + } + Categorization::StaticItem => { + // Do we want to do something here? + None } - _ => None } } } @@ -1282,9 +1281,6 @@ pub enum Aliasability { #[derive(Copy, Clone, Debug)] pub enum AliasableReason { AliasableBorrowed, - AliasableClosure(ast::NodeId), // Aliasable due to capture Fn closure env - AliasableOther, - UnaliasableImmutable, // Created as needed upon seeing ImmutableUnique AliasableStatic, AliasableStaticMut, } @@ -1324,23 +1320,13 @@ impl<'tcx> cmt_<'tcx> { Categorization::Deref(ref b, _, Implicit(ty::MutBorrow, _)) | Categorization::Deref(ref b, _, BorrowedPtr(ty::UniqueImmBorrow, _)) | Categorization::Deref(ref b, _, Implicit(ty::UniqueImmBorrow, _)) | + Categorization::Deref(ref b, _, Unique) | Categorization::Downcast(ref b, _) | Categorization::Interior(ref b, _) => { // Aliasability depends on base cmt b.freely_aliasable() } - Categorization::Deref(ref b, _, Unique) => { - let sub = b.freely_aliasable(); - if b.mutbl.is_mutable() { - // Aliasability depends on base cmt alone - sub - } else { - // Do not allow mutation through an immutable box. - ImmutableUnique(Box::new(sub)) - } - } - Categorization::Rvalue(..) | Categorization::Local(..) | Categorization::Upvar(..) | @@ -1356,13 +1342,9 @@ impl<'tcx> cmt_<'tcx> { } } - Categorization::Deref(ref base, _, BorrowedPtr(ty::ImmBorrow, _)) | - Categorization::Deref(ref base, _, Implicit(ty::ImmBorrow, _)) => { - match base.cat { - Categorization::Upvar(Upvar{ id, .. }) => - FreelyAliasable(AliasableClosure(id.closure_expr_id)), - _ => FreelyAliasable(AliasableBorrowed) - } + Categorization::Deref(_, _, BorrowedPtr(ty::ImmBorrow, _)) | + Categorization::Deref(_, _, Implicit(ty::ImmBorrow, _)) => { + FreelyAliasable(AliasableBorrowed) } } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 28b6c7a13f171..cedb9e1cd1cff 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -188,14 +188,6 @@ fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, // user knows what they're doing in these cases. Ok(()) } - (mc::Aliasability::ImmutableUnique(_), ty::MutBorrow) => { - bccx.report_aliasability_violation( - borrow_span, - loan_cause, - mc::AliasableReason::UnaliasableImmutable, - cmt); - Err(()) - } (mc::Aliasability::FreelyAliasable(alias_cause), ty::UniqueImmBorrow) | (mc::Aliasability::FreelyAliasable(alias_cause), ty::MutBorrow) => { bccx.report_aliasability_violation( @@ -510,4 +502,3 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { self.move_error_collector.report_potential_errors(self.bccx); } } - diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 20d495976b05f..0981369ace5c1 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -34,6 +34,7 @@ use rustc::hir::def_id::DefId; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; use rustc::middle::mem_categorization::Categorization; +use rustc::middle::mem_categorization::ImmutabilityBlame; use rustc::middle::region; use rustc::ty::{self, TyCtxt}; @@ -41,6 +42,7 @@ use std::fmt; use std::rc::Rc; use std::hash::{Hash, Hasher}; use syntax::ast; +use syntax::symbol::keywords; use syntax_pos::{MultiSpan, Span}; use errors::DiagnosticBuilder; @@ -659,12 +661,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { self.tcx.sess.span_err_with_code(s, msg, code); } - pub fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> { + fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> { let span = err.span.clone(); - let mut immutable_field = None; - let mut local_def = None; - let msg = &match err.code { + let msg = match err.code { err_mutbl => { let descr = match err.cmt.note { mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => { @@ -700,27 +700,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { BorrowViolation(euv::AutoUnsafe) | BorrowViolation(euv::ForLoop) | BorrowViolation(euv::MatchDiscriminant) => { - // Check for this field's definition to see if it is an immutable reference - // and suggest making it mutable if that is the case. - immutable_field = err.cmt.get_field_name() - .and_then(|name| err.cmt.get_field(name)) - .and_then(|did| self.tcx.hir.as_local_node_id(did)) - .and_then(|nid| { - if let hir_map::Node::NodeField(ref field) = self.tcx.hir.get(nid) { - return self.suggest_mut_for_immutable(&field.ty) - .map(|msg| (self.tcx.hir.span(nid), msg)); - } - None - }); - local_def = err.cmt.get_def() - .and_then(|nid| { - if !self.tcx.hir.is_argument(nid) { - Some(self.tcx.hir.span(nid)) - } else { - None - } - }); - format!("cannot borrow {} as mutable", descr) } BorrowViolation(euv::ClosureInvocation) => { @@ -746,16 +725,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } }; - let mut db = self.struct_span_err(span, msg); - if let Some((span, msg)) = immutable_field { - db.span_label(span, &msg); - } - if let Some(let_span) = local_def { - if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) { - db.span_label(let_span, &format!("consider changing this to `mut {}`", snippet)); - } - } - db + self.struct_span_err(span, &msg) } pub fn report_aliasability_violation(&self, @@ -788,55 +758,49 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } }; - let mut err = match cause { - mc::AliasableOther => { - struct_span_err!( - self.tcx.sess, span, E0385, - "{} in an aliasable location", prefix) - } - mc::AliasableReason::UnaliasableImmutable => { - struct_span_err!( - self.tcx.sess, span, E0386, - "{} in an immutable container", prefix) + match cause { + mc::AliasableStatic | + mc::AliasableStaticMut => { + // This path cannot occur. It happens when we have an + // `&mut` or assignment to a static. But in the case + // of `static X`, we get a mutability violation first, + // and never get here. In the case of `static mut X`, + // that is unsafe and hence the aliasability error is + // ignored. + span_bug!(span, "aliasability violation for static `{}`", prefix) } - mc::AliasableClosure(id) => { + mc::AliasableBorrowed => {} + }; + let blame = cmt.immutability_blame(); + let mut err = match blame { + Some(ImmutabilityBlame::ClosureEnv(id)) => { let mut err = struct_span_err!( self.tcx.sess, span, E0387, "{} in a captured outer variable in an `Fn` closure", prefix); - if let BorrowViolation(euv::ClosureCapture(_)) = kind { + + // FIXME: the distinction between these 2 messages looks wrong. + let help = if let BorrowViolation(euv::ClosureCapture(_)) = kind { // The aliasability violation with closure captures can // happen for nested closures, so we know the enclosing // closure incorrectly accepts an `Fn` while it needs to // be `FnMut`. - span_help!(&mut err, self.tcx.hir.span(id), - "consider changing this to accept closures that implement `FnMut`"); + "consider changing this to accept closures that implement `FnMut`" + } else { - span_help!(&mut err, self.tcx.hir.span(id), - "consider changing this closure to take self by mutable reference"); - } + "consider changing this closure to take self by mutable reference" + }; + err.span_help(self.tcx.hir.span(id), help); err } - mc::AliasableStatic | - mc::AliasableStaticMut => { - // This path cannot occur. It happens when we have an - // `&mut` or assignment to a static. But in the case - // of `static X`, we get a mutability violation first, - // and never get here. In the case of `static mut X`, - // that is unsafe and hence the aliasability error is - // ignored. - span_bug!(span, "aliasability violation for static `{}`", prefix) - } - mc::AliasableBorrowed => { - let mut e = struct_span_err!( + _ => { + let mut err = struct_span_err!( self.tcx.sess, span, E0389, "{} in a `&` reference", prefix); - e.span_label(span, &"assignment into an immutable reference"); - if let Some(nid) = cmt.get_arg_if_immutable(&self.tcx.hir) { - self.immutable_argument_should_be_mut(nid, &mut e); - } - e + err.span_label(span, &"assignment into an immutable reference"); + err } }; + self.note_immutability_blame(&mut err, blame); if is_closure { err.help("closures behind references must be called via `&mut`"); @@ -873,8 +837,20 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { None } - fn immutable_argument_should_be_mut(&self, nid: ast::NodeId, db: &mut DiagnosticBuilder) { - let parent = self.tcx.hir.get_parent_node(nid); + fn local_binding_mode(&self, node_id: ast::NodeId) -> hir::BindingMode { + let pat = match self.tcx.hir.get(node_id) { + hir_map::Node::NodeLocal(pat) => pat, + node => bug!("bad node for local: {:?}", node) + }; + + match pat.node { + hir::PatKind::Binding(mode, ..) => mode, + _ => bug!("local is not a binding: {:?}", pat) + } + } + + fn local_ty(&self, node_id: ast::NodeId) -> Option<&hir::Ty> { + let parent = self.tcx.hir.get_parent_node(node_id); let parent_node = self.tcx.hir.get(parent); // The parent node is like a fn @@ -882,12 +858,72 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { // `nid`'s parent's `Body` let fn_body = self.tcx.hir.body(fn_like.body()); // Get the position of `nid` in the arguments list - let arg_pos = fn_body.arguments.iter().position(|arg| arg.pat.id == nid); + let arg_pos = fn_body.arguments.iter().position(|arg| arg.pat.id == node_id); if let Some(i) = arg_pos { // The argument's `Ty` - let arg_ty = &fn_like.decl().inputs[i]; - if let Some(msg) = self.suggest_mut_for_immutable(&arg_ty) { - db.span_label(arg_ty.span, &msg); + Some(&fn_like.decl().inputs[i]) + } else { + None + } + } else { + None + } + } + + fn note_immutability_blame(&self, + db: &mut DiagnosticBuilder, + blame: Option) { + match blame { + None => {} + Some(ImmutabilityBlame::ClosureEnv(_)) => {} + Some(ImmutabilityBlame::ImmLocal(node_id)) => { + let let_span = self.tcx.hir.span(node_id); + if let hir::BindingMode::BindByValue(..) = self.local_binding_mode(node_id) { + if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) { + if self.tcx.hir.name(node_id) == keywords::SelfValue.name() && + snippet != "self" { + // avoid suggesting `mut &self`. + return + } + db.span_label( + let_span, + &format!("consider changing this to `mut {}`", snippet) + ); + } + } + } + Some(ImmutabilityBlame::LocalDeref(node_id)) => { + let let_span = self.tcx.hir.span(node_id); + match self.local_binding_mode(node_id) { + hir::BindingMode::BindByRef(..) => { + let snippet = self.tcx.sess.codemap().span_to_snippet(let_span); + if let Ok(snippet) = snippet { + db.span_label( + let_span, + &format!("consider changing this to `{}`", + snippet.replace("ref ", "ref mut ")) + ); + } + } + hir::BindingMode::BindByValue(..) => { + if let Some(local_ty) = self.local_ty(node_id) { + if let Some(msg) = self.suggest_mut_for_immutable(local_ty) { + db.span_label(local_ty.span, &msg); + } + } + } + } + } + Some(ImmutabilityBlame::AdtFieldDeref(_, field)) => { + let node_id = match self.tcx.hir.as_local_node_id(field.did) { + Some(node_id) => node_id, + None => return + }; + + if let hir_map::Node::NodeField(ref field) = self.tcx.hir.get(node_id) { + if let Some(msg) = self.suggest_mut_for_immutable(&field.ty) { + db.span_label(field.ty.span, &msg); + } } } } @@ -941,10 +977,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } } - pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) { + fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) { let error_span = err.span.clone(); match err.code { - err_mutbl => self.note_and_explain_mutbl_error(db, &err, &error_span), + err_mutbl => { + self.note_and_explain_mutbl_error(db, &err, &error_span); + self.note_immutability_blame(db, err.cmt.immutability_blame()); + } err_out_of_scope(super_scope, sub_scope, cause) => { let (value_kind, value_msg) = match err.cmt.cat { mc::Categorization::Rvalue(..) => @@ -1096,13 +1135,6 @@ before rustc 1.16, this temporary lived longer - see issue #39283 \ _ => { if let Categorization::Deref(..) = err.cmt.cat { db.span_label(*error_span, &"cannot borrow as mutable"); - if let Some(local_id) = err.cmt.get_arg_if_immutable(&self.tcx.hir) { - self.immutable_argument_should_be_mut(local_id, db); - } else if let Categorization::Deref(ref inner_cmt, ..) = err.cmt.cat { - if let Categorization::Local(local_id) = inner_cmt.cat { - self.immutable_argument_should_be_mut(local_id, db); - } - } } else if let Categorization::Local(local_id) = err.cmt.cat { let span = self.tcx.hir.span(local_id); if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { @@ -1110,14 +1142,6 @@ before rustc 1.16, this temporary lived longer - see issue #39283 \ db.span_label(*error_span, &format!("cannot reborrow mutably")); db.span_label(*error_span, &format!("try removing `&mut` here")); } else { - if snippet.starts_with("ref ") { - db.span_label(span, &format!("use `{}` here to make mutable", - snippet.replace("ref ", "ref mut "))); - } else if snippet != "self" { - db.span_label(span, - &format!("use `mut {}` here to make mutable", - snippet)); - } db.span_label(*error_span, &format!("cannot borrow mutably")); } } else { diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index db4a1701e976b..bfd342a9f2134 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -198,7 +198,7 @@ fn main() { ``` "##, -E0386: r##" +/*E0386: r##" This error occurs when an attempt is made to mutate the target of a mutable reference stored inside an immutable container. @@ -228,7 +228,7 @@ let x: i64 = 1; let y: Box> = Box::new(Cell::new(x)); y.set(2); ``` -"##, +"##,*/ E0387: r##" This error occurs when an attempt is made to mutate or mutably reference data @@ -1117,6 +1117,6 @@ fn main() { } register_diagnostics! { - E0385, // {} in an aliasable location +// E0385, // {} in an aliasable location E0524, // two closures require unique access to `..` at the same time } diff --git a/src/test/compile-fail/augmented-assignments.rs b/src/test/compile-fail/augmented-assignments.rs index 92a8b10669cee..736aa465aa732 100644 --- a/src/test/compile-fail/augmented-assignments.rs +++ b/src/test/compile-fail/augmented-assignments.rs @@ -27,7 +27,7 @@ fn main() { x; //~ value moved here let y = Int(2); - //~^use `mut y` here to make mutable + //~^ consider changing this to `mut y` y //~ error: cannot borrow immutable local variable `y` as mutable //~| cannot borrow += diff --git a/src/test/compile-fail/borrowck/borrowck-issue-14498.rs b/src/test/compile-fail/borrowck/borrowck-issue-14498.rs index 64033623fe2d1..8b7ccedd6974f 100644 --- a/src/test/compile-fail/borrowck/borrowck-issue-14498.rs +++ b/src/test/compile-fail/borrowck/borrowck-issue-14498.rs @@ -23,7 +23,7 @@ fn indirect_write_to_imm_box() { let mut x: isize = 1; let y: Box<_> = box &mut x; let p = &y; - ***p = 2; //~ ERROR cannot assign to data in an immutable container + ***p = 2; //~ ERROR cannot assign to data in a `&` reference drop(p); } @@ -43,7 +43,6 @@ fn borrow_in_var_from_var_via_imm_box() { let p = &y; let q = &***p; **y = 2; //~ ERROR cannot assign to `**y` because it is borrowed - //~^ ERROR cannot assign to data in an immutable container drop(p); drop(q); } @@ -64,7 +63,6 @@ fn borrow_in_var_from_field_via_imm_box() { let p = &y; let q = &***p; **y = 2; //~ ERROR cannot assign to `**y` because it is borrowed - //~^ ERROR cannot assign to data in an immutable container drop(p); drop(q); } @@ -85,7 +83,6 @@ fn borrow_in_field_from_var_via_imm_box() { let p = &y.a; let q = &***p; **y.a = 2; //~ ERROR cannot assign to `**y.a` because it is borrowed - //~^ ERROR cannot assign to data in an immutable container drop(p); drop(q); } @@ -106,7 +103,6 @@ fn borrow_in_field_from_field_via_imm_box() { let p = &y.a; let q = &***p; **y.a = 2; //~ ERROR cannot assign to `**y.a` because it is borrowed - //~^ ERROR cannot assign to data in an immutable container drop(p); drop(q); } diff --git a/src/test/compile-fail/issue-33819.rs b/src/test/compile-fail/issue-33819.rs index 9c9677c1e9863..499e7e54947b7 100644 --- a/src/test/compile-fail/issue-33819.rs +++ b/src/test/compile-fail/issue-33819.rs @@ -12,7 +12,7 @@ fn main() { match op { Some(ref v) => { let a = &mut v; }, //~^ ERROR:cannot borrow immutable - //~| use `ref mut v` here to make mutable + //~| cannot borrow mutably None => {}, } } diff --git a/src/test/compile-fail/mut-suggestion.rs b/src/test/compile-fail/mut-suggestion.rs index 242ad7aee8d12..0015c8e5c00a1 100644 --- a/src/test/compile-fail/mut-suggestion.rs +++ b/src/test/compile-fail/mut-suggestion.rs @@ -17,7 +17,7 @@ impl S { } fn func(arg: S) { - //~^ here to make mutable + //~^ consider changing this to `mut arg` arg.mutate(); //~^ ERROR cannot borrow immutable argument //~| cannot borrow mutably @@ -25,7 +25,7 @@ fn func(arg: S) { fn main() { let local = S; - //~^ here to make mutable + //~^ consider changing this to `mut local` local.mutate(); //~^ ERROR cannot borrow immutable local variable //~| cannot borrow mutably diff --git a/src/test/ui/codemap_tests/huge_multispan_highlight.stderr b/src/test/ui/codemap_tests/huge_multispan_highlight.stderr index edbfd72df615f..7bb69caa1024a 100644 --- a/src/test/ui/codemap_tests/huge_multispan_highlight.stderr +++ b/src/test/ui/codemap_tests/huge_multispan_highlight.stderr @@ -2,7 +2,7 @@ error: cannot borrow immutable local variable `x` as mutable --> $DIR/huge_multispan_highlight.rs:100:18 | 12 | let x = "foo"; - | - use `mut x` here to make mutable + | - consider changing this to `mut x` ... 100 | let y = &mut x; | ^ cannot borrow mutably diff --git a/src/test/ui/did_you_mean/issue-31424.stderr b/src/test/ui/did_you_mean/issue-31424.stderr index 4873acf551ebc..60fa06d314ff7 100644 --- a/src/test/ui/did_you_mean/issue-31424.stderr +++ b/src/test/ui/did_you_mean/issue-31424.stderr @@ -10,6 +10,8 @@ error: cannot borrow immutable argument `self` as mutable error: cannot borrow immutable argument `self` as mutable --> $DIR/issue-31424.rs:23:15 | +22 | fn bar(self: &mut Self) { + | ---- consider changing this to `mut self` 23 | (&mut self).bar(); | ^^^^ cannot borrow mutably diff --git a/src/test/ui/did_you_mean/issue-35937.rs b/src/test/ui/did_you_mean/issue-35937.rs new file mode 100644 index 0000000000000..9ec8728fd32c1 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-35937.rs @@ -0,0 +1,31 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + pub v: Vec +} + +fn main() { + let f = Foo { v: Vec::new() }; + f.v.push("cat".to_string()); +} + + +struct S { + x: i32, +} +fn foo() { + let s = S { x: 42 }; + s.x += 1; +} + +fn bar(s: S) { + s.x += 1; +} diff --git a/src/test/ui/did_you_mean/issue-35937.stderr b/src/test/ui/did_you_mean/issue-35937.stderr new file mode 100644 index 0000000000000..bea3d1291433d --- /dev/null +++ b/src/test/ui/did_you_mean/issue-35937.stderr @@ -0,0 +1,26 @@ +error: cannot borrow immutable field `f.v` as mutable + --> $DIR/issue-35937.rs:17:5 + | +16 | let f = Foo { v: Vec::new() }; + | - consider changing this to `mut f` +17 | f.v.push("cat".to_string()); + | ^^^ cannot mutably borrow immutable field + +error: cannot assign to immutable field `s.x` + --> $DIR/issue-35937.rs:26:5 + | +25 | let s = S { x: 42 }; + | - consider changing this to `mut s` +26 | s.x += 1; + | ^^^^^^^^ cannot mutably borrow immutable field + +error: cannot assign to immutable field `s.x` + --> $DIR/issue-35937.rs:30:5 + | +29 | fn bar(s: S) { + | - consider changing this to `mut s` +30 | s.x += 1; + | ^^^^^^^^ cannot mutably borrow immutable field + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/did_you_mean/issue-38147-2.stderr b/src/test/ui/did_you_mean/issue-38147-2.stderr index fdaf0cd44d9d4..855feaf7d2d5e 100644 --- a/src/test/ui/did_you_mean/issue-38147-2.stderr +++ b/src/test/ui/did_you_mean/issue-38147-2.stderr @@ -2,7 +2,7 @@ error: cannot borrow immutable borrowed content `*self.s` as mutable --> $DIR/issue-38147-2.rs:17:9 | 12 | s: &'a String - | ------------- use `&'a mut String` here to make mutable + | ---------- use `&'a mut String` here to make mutable ... 17 | self.s.push('x'); | ^^^^^^ cannot borrow as mutable diff --git a/src/test/ui/did_you_mean/issue-38147-3.stderr b/src/test/ui/did_you_mean/issue-38147-3.stderr index d2280fa561bde..d970d078df8d9 100644 --- a/src/test/ui/did_you_mean/issue-38147-3.stderr +++ b/src/test/ui/did_you_mean/issue-38147-3.stderr @@ -2,10 +2,8 @@ error: cannot borrow immutable borrowed content `*self.s` as mutable --> $DIR/issue-38147-3.rs:17:9 | 12 | s: &'a String - | ------------- use `&'a mut String` here to make mutable + | ---------- use `&'a mut String` here to make mutable ... -16 | fn f(&self) { - | ----- use `&mut self` here to make mutable 17 | self.s.push('x'); | ^^^^^^ cannot borrow as mutable diff --git a/src/test/ui/did_you_mean/issue-39544.rs b/src/test/ui/did_you_mean/issue-39544.rs index bcdafefa2472b..c98fed3c3c3c7 100644 --- a/src/test/ui/did_you_mean/issue-39544.rs +++ b/src/test/ui/did_you_mean/issue-39544.rs @@ -8,15 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -enum X { +pub enum X { Y } -struct Z { +pub struct Z { x: X } -fn main() { +pub fn main() { let z = Z { x: X::Y }; let _ = &mut z.x; } + +pub fn with_arg(z: Z, w: &Z) { + let _ = &mut z.x; + let _ = &mut w.x; +} diff --git a/src/test/ui/did_you_mean/issue-39544.stderr b/src/test/ui/did_you_mean/issue-39544.stderr index 7f124e6d34d35..bcd55fd744bb6 100644 --- a/src/test/ui/did_you_mean/issue-39544.stderr +++ b/src/test/ui/did_you_mean/issue-39544.stderr @@ -6,5 +6,22 @@ error: cannot borrow immutable field `z.x` as mutable 21 | let _ = &mut z.x; | ^^^ cannot mutably borrow immutable field -error: aborting due to previous error +error: cannot borrow immutable field `z.x` as mutable + --> $DIR/issue-39544.rs:25:18 + | +24 | pub fn with_arg(z: Z, w: &Z) { + | - consider changing this to `mut z` +25 | let _ = &mut z.x; + | ^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `w.x` as mutable + --> $DIR/issue-39544.rs:26:18 + | +24 | pub fn with_arg(z: Z, w: &Z) { + | -- use `&mut Z` here to make mutable +25 | let _ = &mut z.x; +26 | let _ = &mut w.x; + | ^^^ cannot mutably borrow immutable field + +error: aborting due to 3 previous errors diff --git a/src/test/ui/did_you_mean/issue-40823.rs b/src/test/ui/did_you_mean/issue-40823.rs new file mode 100644 index 0000000000000..f4ae325727982 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-40823.rs @@ -0,0 +1,14 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let mut buf = &[1, 2, 3, 4]; + buf.iter_mut(); +} diff --git a/src/test/ui/did_you_mean/issue-40823.stderr b/src/test/ui/did_you_mean/issue-40823.stderr new file mode 100644 index 0000000000000..8e77ebd9b6da3 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-40823.stderr @@ -0,0 +1,8 @@ +error: cannot borrow immutable borrowed content `*buf` as mutable + --> $DIR/issue-40823.rs:13:5 + | +13 | buf.iter_mut(); + | ^^^ cannot borrow as mutable + +error: aborting due to previous error + diff --git a/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr b/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr index b83a6aaebf323..edf1635a6b84f 100644 --- a/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr +++ b/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr @@ -2,7 +2,7 @@ error: cannot borrow immutable argument `x` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:63:24 | 62 | fn deref_mut_field1(x: Own) { - | - use `mut x` here to make mutable + | - consider changing this to `mut x` 63 | let __isize = &mut x.y; //~ ERROR cannot borrow | ^ cannot borrow mutably @@ -28,7 +28,7 @@ error: cannot borrow immutable argument `x` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:98:5 | 97 | fn assign_field1<'a>(x: Own) { - | - use `mut x` here to make mutable + | - consider changing this to `mut x` 98 | x.y = 3; //~ ERROR cannot borrow | ^ cannot borrow mutably @@ -54,7 +54,7 @@ error: cannot borrow immutable argument `x` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:119:5 | 118 | fn deref_mut_method1(x: Own) { - | - use `mut x` here to make mutable + | - consider changing this to `mut x` 119 | x.set(0, 0); //~ ERROR cannot borrow | ^ cannot borrow mutably @@ -70,7 +70,7 @@ error: cannot borrow immutable argument `x` as mutable --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:139:6 | 138 | fn assign_method1<'a>(x: Own) { - | - use `mut x` here to make mutable + | - consider changing this to `mut x` 139 | *x.y_mut() = 3; //~ ERROR cannot borrow | ^ cannot borrow mutably diff --git a/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.stderr b/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.stderr index af954a4d7924f..2ec0116872179 100644 --- a/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.stderr +++ b/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.stderr @@ -2,7 +2,7 @@ error: cannot borrow immutable argument `x` as mutable --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:39:25 | 38 | fn deref_mut1(x: Own) { - | - use `mut x` here to make mutable + | - consider changing this to `mut x` 39 | let __isize = &mut *x; //~ ERROR cannot borrow | ^ cannot borrow mutably @@ -18,7 +18,7 @@ error: cannot borrow immutable argument `x` as mutable --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:59:6 | 58 | fn assign1<'a>(x: Own) { - | - use `mut x` here to make mutable + | - consider changing this to `mut x` 59 | *x = 3; //~ ERROR cannot borrow | ^ cannot borrow mutably diff --git a/src/test/ui/span/borrowck-object-mutability.stderr b/src/test/ui/span/borrowck-object-mutability.stderr index 4ef1cb9c239e4..0abdbdc3a21b5 100644 --- a/src/test/ui/span/borrowck-object-mutability.stderr +++ b/src/test/ui/span/borrowck-object-mutability.stderr @@ -10,6 +10,9 @@ error: cannot borrow immutable borrowed content `*x` as mutable error: cannot borrow immutable `Box` content `*x` as mutable --> $DIR/borrowck-object-mutability.rs:29:5 | +27 | fn owned_receiver(x: Box) { + | - consider changing this to `mut x` +28 | x.borrowed(); 29 | x.borrowed_mut(); //~ ERROR cannot borrow | ^ cannot borrow as mutable From 39011f8590b69d5ee9037c4ac9b863a516ae2e1e Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 27 Mar 2017 01:35:50 +0300 Subject: [PATCH 2/2] fix handling of `self` --- src/librustc/hir/lowering.rs | 7 ++ src/librustc/hir/mod.rs | 3 + src/librustc_borrowck/borrowck/mod.rs | 60 ++++++++------- src/test/ui/did_you_mean/issue-39544.rs | 28 ++++++- src/test/ui/did_you_mean/issue-39544.stderr | 83 +++++++++++++++++++-- 5 files changed, 143 insertions(+), 38 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 2ac1a036f99e1..726c305e90304 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -907,6 +907,13 @@ impl<'a> LoweringContext<'a> { FunctionRetTy::Default(span) => hir::DefaultReturn(span), }, variadic: decl.variadic, + has_implicit_self: decl.inputs.get(0).map_or(false, |arg| { + match arg.ty.node { + TyKind::ImplicitSelf => true, + TyKind::Rptr(_, ref mt) => mt.ty.node == TyKind::ImplicitSelf, + _ => false + } + }) }) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 1c79a02d3da0e..43b7deb5b90e3 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1379,6 +1379,9 @@ pub struct FnDecl { pub inputs: HirVec>, pub output: FunctionRetTy, pub variadic: bool, + /// True if this function has an `self`, `&self` or `&mut self` receiver + /// (but not a `self: Xxx` one). + pub has_implicit_self: bool, } #[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 0981369ace5c1..558a835be1884 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -42,7 +42,6 @@ use std::fmt; use std::rc::Rc; use std::hash::{Hash, Hasher}; use syntax::ast; -use syntax::symbol::keywords; use syntax_pos::{MultiSpan, Span}; use errors::DiagnosticBuilder; @@ -809,32 +808,33 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } /// Given a type, if it is an immutable reference, return a suggestion to make it mutable - fn suggest_mut_for_immutable(&self, pty: &hir::Ty) -> Option { + fn suggest_mut_for_immutable(&self, pty: &hir::Ty, is_implicit_self: bool) -> Option { // Check wether the argument is an immutable reference + debug!("suggest_mut_for_immutable({:?}, {:?})", pty, is_implicit_self); if let hir::TyRptr(lifetime, hir::MutTy { mutbl: hir::Mutability::MutImmutable, ref ty }) = pty.node { // Account for existing lifetimes when generating the message - if !lifetime.is_elided() { - if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(ty.span) { - if let Ok(lifetime_snippet) = self.tcx.sess.codemap() - .span_to_snippet(lifetime.span) { - return Some(format!("use `&{} mut {}` here to make mutable", - lifetime_snippet, - snippet)); - } - } - } else if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(pty.span) { - if snippet.starts_with("&") { - return Some(format!("use `{}` here to make mutable", - snippet.replace("&", "&mut "))); - } + let pointee_snippet = match self.tcx.sess.codemap().span_to_snippet(ty.span) { + Ok(snippet) => snippet, + _ => return None + }; + + let lifetime_snippet = if !lifetime.is_elided() { + format!("{} ", match self.tcx.sess.codemap().span_to_snippet(lifetime.span) { + Ok(lifetime_snippet) => lifetime_snippet, + _ => return None + }) } else { - bug!("couldn't find a snippet for span: {:?}", pty.span); - } + String::new() + }; + Some(format!("use `&{}mut {}` here to make mutable", + lifetime_snippet, + if is_implicit_self { "self" } else { &*pointee_snippet })) + } else { + None } - None } fn local_binding_mode(&self, node_id: ast::NodeId) -> hir::BindingMode { @@ -849,7 +849,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } } - fn local_ty(&self, node_id: ast::NodeId) -> Option<&hir::Ty> { + fn local_ty(&self, node_id: ast::NodeId) -> (Option<&hir::Ty>, bool) { let parent = self.tcx.hir.get_parent_node(node_id); let parent_node = self.tcx.hir.get(parent); @@ -857,16 +857,17 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { if let Some(fn_like) = FnLikeNode::from_node(parent_node) { // `nid`'s parent's `Body` let fn_body = self.tcx.hir.body(fn_like.body()); - // Get the position of `nid` in the arguments list + // Get the position of `node_id` in the arguments list let arg_pos = fn_body.arguments.iter().position(|arg| arg.pat.id == node_id); if let Some(i) = arg_pos { // The argument's `Ty` - Some(&fn_like.decl().inputs[i]) + (Some(&fn_like.decl().inputs[i]), + i == 0 && fn_like.decl().has_implicit_self) } else { - None + (None, false) } } else { - None + (None, false) } } @@ -880,8 +881,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { let let_span = self.tcx.hir.span(node_id); if let hir::BindingMode::BindByValue(..) = self.local_binding_mode(node_id) { if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(let_span) { - if self.tcx.hir.name(node_id) == keywords::SelfValue.name() && - snippet != "self" { + let (_, is_implicit_self) = self.local_ty(node_id); + if is_implicit_self && snippet != "self" { // avoid suggesting `mut &self`. return } @@ -906,8 +907,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { } } hir::BindingMode::BindByValue(..) => { - if let Some(local_ty) = self.local_ty(node_id) { - if let Some(msg) = self.suggest_mut_for_immutable(local_ty) { + if let (Some(local_ty), is_implicit_self) = self.local_ty(node_id) { + if let Some(msg) = + self.suggest_mut_for_immutable(local_ty, is_implicit_self) { db.span_label(local_ty.span, &msg); } } @@ -921,7 +923,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { }; if let hir_map::Node::NodeField(ref field) = self.tcx.hir.get(node_id) { - if let Some(msg) = self.suggest_mut_for_immutable(&field.ty) { + if let Some(msg) = self.suggest_mut_for_immutable(&field.ty, false) { db.span_label(field.ty.span, &msg); } } diff --git a/src/test/ui/did_you_mean/issue-39544.rs b/src/test/ui/did_you_mean/issue-39544.rs index c98fed3c3c3c7..6331fc5771fcb 100644 --- a/src/test/ui/did_you_mean/issue-39544.rs +++ b/src/test/ui/did_you_mean/issue-39544.rs @@ -16,11 +16,37 @@ pub struct Z { x: X } -pub fn main() { +fn main() { let z = Z { x: X::Y }; let _ = &mut z.x; } +impl Z { + fn foo<'z>(&'z self) { + let _ = &mut self.x; + } + + fn foo1(&self, other: &Z) { + let _ = &mut self.x; + let _ = &mut other.x; + } + + fn foo2<'a>(&'a self, other: &Z) { + let _ = &mut self.x; + let _ = &mut other.x; + } + + fn foo3<'a>(self: &'a Self, other: &Z) { + let _ = &mut self.x; + let _ = &mut other.x; + } + + fn foo4(other: &Z) { + let _ = &mut other.x; + } + +} + pub fn with_arg(z: Z, w: &Z) { let _ = &mut z.x; let _ = &mut w.x; diff --git a/src/test/ui/did_you_mean/issue-39544.stderr b/src/test/ui/did_you_mean/issue-39544.stderr index bcd55fd744bb6..e1e229a8b0572 100644 --- a/src/test/ui/did_you_mean/issue-39544.stderr +++ b/src/test/ui/did_you_mean/issue-39544.stderr @@ -6,22 +6,89 @@ error: cannot borrow immutable field `z.x` as mutable 21 | let _ = &mut z.x; | ^^^ cannot mutably borrow immutable field +error: cannot borrow immutable field `self.x` as mutable + --> $DIR/issue-39544.rs:26:22 + | +25 | fn foo<'z>(&'z self) { + | -------- use `&'z mut self` here to make mutable +26 | let _ = &mut self.x; + | ^^^^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `self.x` as mutable + --> $DIR/issue-39544.rs:30:22 + | +29 | fn foo1(&self, other: &Z) { + | ----- use `&mut self` here to make mutable +30 | let _ = &mut self.x; + | ^^^^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `other.x` as mutable + --> $DIR/issue-39544.rs:31:22 + | +29 | fn foo1(&self, other: &Z) { + | -- use `&mut Z` here to make mutable +30 | let _ = &mut self.x; +31 | let _ = &mut other.x; + | ^^^^^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `self.x` as mutable + --> $DIR/issue-39544.rs:35:22 + | +34 | fn foo2<'a>(&'a self, other: &Z) { + | -------- use `&'a mut self` here to make mutable +35 | let _ = &mut self.x; + | ^^^^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `other.x` as mutable + --> $DIR/issue-39544.rs:36:22 + | +34 | fn foo2<'a>(&'a self, other: &Z) { + | -- use `&mut Z` here to make mutable +35 | let _ = &mut self.x; +36 | let _ = &mut other.x; + | ^^^^^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `self.x` as mutable + --> $DIR/issue-39544.rs:40:22 + | +39 | fn foo3<'a>(self: &'a Self, other: &Z) { + | -------- use `&'a mut Self` here to make mutable +40 | let _ = &mut self.x; + | ^^^^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `other.x` as mutable + --> $DIR/issue-39544.rs:41:22 + | +39 | fn foo3<'a>(self: &'a Self, other: &Z) { + | -- use `&mut Z` here to make mutable +40 | let _ = &mut self.x; +41 | let _ = &mut other.x; + | ^^^^^^^ cannot mutably borrow immutable field + +error: cannot borrow immutable field `other.x` as mutable + --> $DIR/issue-39544.rs:45:22 + | +44 | fn foo4(other: &Z) { + | -- use `&mut Z` here to make mutable +45 | let _ = &mut other.x; + | ^^^^^^^ cannot mutably borrow immutable field + error: cannot borrow immutable field `z.x` as mutable - --> $DIR/issue-39544.rs:25:18 + --> $DIR/issue-39544.rs:51:18 | -24 | pub fn with_arg(z: Z, w: &Z) { +50 | pub fn with_arg(z: Z, w: &Z) { | - consider changing this to `mut z` -25 | let _ = &mut z.x; +51 | let _ = &mut z.x; | ^^^ cannot mutably borrow immutable field error: cannot borrow immutable field `w.x` as mutable - --> $DIR/issue-39544.rs:26:18 + --> $DIR/issue-39544.rs:52:18 | -24 | pub fn with_arg(z: Z, w: &Z) { +50 | pub fn with_arg(z: Z, w: &Z) { | -- use `&mut Z` here to make mutable -25 | let _ = &mut z.x; -26 | let _ = &mut w.x; +51 | let _ = &mut z.x; +52 | let _ = &mut w.x; | ^^^ cannot mutably borrow immutable field -error: aborting due to 3 previous errors +error: aborting due to 11 previous errors