diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 3536947b25ebfbdba7e75f2049751a82deced06d..272f5024f9f1a5293bbc94c77b44f73b6388fe7f 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1591,7 +1591,10 @@ fn check_if_reassignment_to_immutable_state( if let Some(&init_index) = first_init_index { // And, if so, report an error. let init = &self.move_data.inits[init_index]; - self.report_illegal_reassignment(context, place_span, init.span, place_span.0); + let span = init.span(&self.mir); + self.report_illegal_reassignment( + context, place_span, span, place_span.0 + ); } } diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs index f96ef909c0df8ace584c08ca153c59455446e851..78ab772d9ad5b2f3ed5fd9cbd2cbf26d62f53c50 100644 --- a/src/librustc_mir/borrow_check/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/mutability_errors.rs @@ -10,18 +10,20 @@ use rustc::hir; use rustc::hir::Node; -use rustc::mir::{self, BindingForm, ClearCrossCrate, Local, Location, Mir}; -use rustc::mir::{Mutability, Place, Projection, ProjectionElem, Static}; -use rustc::ty::{self, TyCtxt}; +use rustc::mir::{self, BindingForm, Constant, ClearCrossCrate, Local, Location, Mir}; +use rustc::mir::{Mutability, Operand, Place, Projection, ProjectionElem, Static, Terminator}; +use rustc::mir::TerminatorKind; +use rustc::ty::{self, Const, DefIdTree, TyS, TyKind, TyCtxt}; use rustc_data_structures::indexed_vec::Idx; use syntax_pos::Span; +use dataflow::move_paths::InitLocation; use borrow_check::MirBorrowckCtxt; use util::borrowck_errors::{BorrowckErrors, Origin}; use util::collect_writes::FindAssignments; use util::suggest_ref_mut; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(super) enum AccessKind { MutableBorrow, Mutate, @@ -393,6 +395,63 @@ pub(super) fn report_mutability_error( ); } + Place::Projection(box Projection { + base: Place::Local(local), + elem: ProjectionElem::Deref, + }) if error_access == AccessKind::MutableBorrow => { + err.span_label(span, format!("cannot {ACT}", ACT = act)); + + let mpi = self.move_data.rev_lookup.find_local(*local); + for i in self.move_data.init_path_map[mpi].iter() { + if let InitLocation::Statement(location) = self.move_data.inits[*i].location { + if let Some( + Terminator { + kind: TerminatorKind::Call { + func: Operand::Constant(box Constant { + literal: Const { + ty: &TyS { + sty: TyKind::FnDef(id, substs), + .. + }, + .. + }, + .. + }), + .. + }, + .. + } + ) = &self.mir.basic_blocks()[location.block].terminator { + if self.tcx.parent(id) == self.tcx.lang_items().index_trait() { + + let mut found = false; + self.tcx.for_each_relevant_impl( + self.tcx.lang_items().index_mut_trait().unwrap(), + substs.type_at(0), + |_relevant_impl| { + found = true; + } + ); + + let extra = if found { + String::from("") + } else { + format!(", but it is not implemented for `{}`", + substs.type_at(0)) + }; + + err.help( + &format!( + "trait `IndexMut` is required to modify indexed content{}", + extra, + ), + ); + } + } + } + } + } + _ => { err.span_label(span, format!("cannot {ACT}", ACT = act)); } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 2148363c79712ee7ab17c0ae86c5cfaaef903491..0162bb84b94b62f1080a1bf416756f5796b0b6d7 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -20,7 +20,7 @@ use super::abs_domain::Lift; use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex}; -use super::{MoveError, InitIndex, Init, LookupResult, InitKind}; +use super::{MoveError, InitIndex, Init, InitLocation, LookupResult, InitKind}; use super::IllegalMoveOriginKind::*; struct MoveDataBuilder<'a, 'gcx: 'tcx, 'tcx: 'a> { @@ -237,10 +237,9 @@ impl<'a, 'gcx, 'tcx> MoveDataBuilder<'a, 'gcx, 'tcx> { fn gather_args(&mut self) { for arg in self.mir.args_iter() { let path = self.data.rev_lookup.locals[arg]; - let span = self.mir.local_decls[arg].source_info.span; let init = self.data.inits.push(Init { - path, span, kind: InitKind::Deep + path, kind: InitKind::Deep, location: InitLocation::Argument(arg), }); debug!("gather_args: adding init {:?} of {:?} for argument {:?}", @@ -428,7 +427,7 @@ fn gather_init(&mut self, place: &Place<'tcx>, kind: InitKind) { if let LookupResult::Exact(path) = self.builder.data.rev_lookup.find(place) { let init = self.builder.data.inits.push(Init { - span: self.builder.mir.source_info(self.loc).span, + location: InitLocation::Statement(self.loc), path, kind, }); diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 7b4cbdf7131b0e67b2c3b78b5c2a3a29210be39a..8728afc228fcc0c60ea3f06773b2dbe7c511d989 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -196,12 +196,21 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { pub struct Init { /// path being initialized pub path: MovePathIndex, - /// span of initialization - pub span: Span, + /// location of initialization + pub location: InitLocation, /// Extra information about this initialization pub kind: InitKind, } + +/// Initializations can be from an argument or from a statement. Arguments +/// do not have locations, in those cases the `Local` is kept.. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum InitLocation { + Argument(Local), + Statement(Location), +} + /// Additional information about the initialization. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum InitKind { @@ -215,7 +224,16 @@ pub enum InitKind { impl fmt::Debug for Init { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{:?}@{:?} ({:?})", self.path, self.span, self.kind) + write!(fmt, "{:?}@{:?} ({:?})", self.path, self.location, self.kind) + } +} + +impl Init { + crate fn span<'gcx>(&self, mir: &Mir<'gcx>) -> Span { + match self.location { + InitLocation::Argument(local) => mir.local_decls[local].source_info.span, + InitLocation::Statement(location) => mir.source_info(location).span, + } } } diff --git a/src/test/ui/borrowck/index-mut-help-with-impl.nll.stderr b/src/test/ui/borrowck/index-mut-help-with-impl.nll.stderr new file mode 100644 index 0000000000000000000000000000000000000000..8e7ffa67b061c68524362f4f9be08f39adb5ab2b --- /dev/null +++ b/src/test/ui/borrowck/index-mut-help-with-impl.nll.stderr @@ -0,0 +1,11 @@ +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/index-mut-help-with-impl.rs:19:5 + | +LL | Index::index(&v, 1..2).make_ascii_uppercase(); //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable + | + = help: trait `IndexMut` is required to modify indexed content + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/borrowck/index-mut-help-with-impl.rs b/src/test/ui/borrowck/index-mut-help-with-impl.rs new file mode 100644 index 0000000000000000000000000000000000000000..a5bab48c5cc581a64a40ef6e26bc0d274a9b3f30 --- /dev/null +++ b/src/test/ui/borrowck/index-mut-help-with-impl.rs @@ -0,0 +1,20 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// When mutably indexing a type that implements `Index` and `IndexMut` but +// `Index::index` is being used specifically, the normal special help message +// should not mention a missing `IndexMut` impl. + +fn main() { + use std::ops::Index; + + let v = String::from("dinosaur"); + Index::index(&v, 1..2).make_ascii_uppercase(); //~ ERROR +} diff --git a/src/test/ui/borrowck/index-mut-help-with-impl.stderr b/src/test/ui/borrowck/index-mut-help-with-impl.stderr new file mode 100644 index 0000000000000000000000000000000000000000..9c28b86c45a92fc00ecf1cb6b2563343e8fc597a --- /dev/null +++ b/src/test/ui/borrowck/index-mut-help-with-impl.stderr @@ -0,0 +1,9 @@ +error[E0596]: cannot borrow immutable borrowed content as mutable + --> $DIR/index-mut-help-with-impl.rs:19:5 + | +LL | Index::index(&v, 1..2).make_ascii_uppercase(); //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/borrowck/index-mut-help.nll.stderr b/src/test/ui/borrowck/index-mut-help.nll.stderr index cc058f1fde5da7cc549b5612a6f0c1ff03367453..a15f37684e73871f460b14d1cc9f9b0663a2d86a 100644 --- a/src/test/ui/borrowck/index-mut-help.nll.stderr +++ b/src/test/ui/borrowck/index-mut-help.nll.stderr @@ -3,6 +3,8 @@ error[E0596]: cannot borrow data in a `&` reference as mutable | LL | map["peter"].clear(); //~ ERROR | ^^^^^^^^^^^^ cannot borrow as mutable + | + = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, std::string::String>` error[E0594]: cannot assign to data in a `&` reference --> $DIR/index-mut-help.rs:22:5 @@ -15,6 +17,8 @@ error[E0596]: cannot borrow data in a `&` reference as mutable | LL | let _ = &mut map["peter"]; //~ ERROR | ^^^^^^^^^^^^^^^^^ cannot borrow as mutable + | + = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, std::string::String>` error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-41726.nll.stderr b/src/test/ui/issues/issue-41726.nll.stderr index 06ff743a0f5ed295a7e360db7bae73e4d9d72c6f..9f727881beaf2390f2cd78a83be8d93d28f540b2 100644 --- a/src/test/ui/issues/issue-41726.nll.stderr +++ b/src/test/ui/issues/issue-41726.nll.stderr @@ -3,6 +3,8 @@ error[E0596]: cannot borrow data in a `&` reference as mutable | LL | things[src.as_str()].sort(); //~ ERROR cannot borrow immutable | ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable + | + = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap>` error: aborting due to previous error