@@ -36,42 +36,166 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
3636 location : Location ,
3737 ) {
3838 let mut err;
39- let item_msg = match self . describe_place ( place) {
40- Some ( name) => format ! ( "immutable item `{}`" , name) ,
41- None => "immutable item" . to_owned ( ) ,
42- } ;
39+ let item_msg;
40+ let reason;
41+ let access_place_desc = self . describe_place ( access_place) ;
42+
43+ match the_place_err {
44+ Place :: Local ( local) => {
45+ item_msg = format ! ( "`{}`" , access_place_desc. unwrap( ) ) ;
46+ if let Place :: Local ( _) = access_place {
47+ reason = ", as it is not declared as mutable" . to_string ( ) ;
48+ } else {
49+ let name = self . mir . local_decls [ * local]
50+ . name
51+ . expect ( "immutable unnamed local" ) ;
52+ reason = format ! ( ", as `{}` is not declared as mutable" , name) ;
53+ }
54+ }
55+
56+ Place :: Projection ( box Projection {
57+ base,
58+ elem : ProjectionElem :: Field ( upvar_index, _) ,
59+ } ) => {
60+ debug_assert ! ( is_closure_or_generator(
61+ base. ty( self . mir, self . tcx) . to_ty( self . tcx)
62+ ) ) ;
63+
64+ item_msg = format ! ( "`{}`" , access_place_desc. unwrap( ) ) ;
65+ if self . is_upvar ( access_place) {
66+ reason = ", as it is not declared as mutable" . to_string ( ) ;
67+ } else {
68+ let name = self . mir . upvar_decls [ upvar_index. index ( ) ] . debug_name ;
69+ reason = format ! ( ", as `{}` is not declared as mutable" , name) ;
70+ }
71+ }
72+
73+ Place :: Projection ( box Projection {
74+ base,
75+ elem : ProjectionElem :: Deref ,
76+ } ) => {
77+ if * base == Place :: Local ( Local :: new ( 1 ) ) && !self . mir . upvar_decls . is_empty ( ) {
78+ item_msg = format ! ( "`{}`" , access_place_desc. unwrap( ) ) ;
79+ debug_assert ! ( self . mir. local_decls[ Local :: new( 1 ) ] . ty. is_region_ptr( ) ) ;
80+ debug_assert ! ( is_closure_or_generator(
81+ the_place_err. ty( self . mir, self . tcx) . to_ty( self . tcx)
82+ ) ) ;
83+
84+ reason = if self . is_upvar ( access_place) {
85+ ", as it is a captured variable in a `Fn` closure" . to_string ( )
86+ } else {
87+ format ! ( ", as `Fn` closures cannot mutate their captured variables" )
88+ }
89+ } else if {
90+ if let Place :: Local ( local) = * base {
91+ if let Some ( ClearCrossCrate :: Set ( BindingForm :: RefForGuard ) )
92+ = self . mir . local_decls [ local] . is_user_variable {
93+ true
94+ } else {
95+ false
96+ }
97+ } else {
98+ false
99+ }
100+ } {
101+ item_msg = format ! ( "`{}`" , access_place_desc. unwrap( ) ) ;
102+ reason = format ! ( ", as it is immutable for the pattern guard" ) ;
103+ } else {
104+ let pointer_type =
105+ if base. ty ( self . mir , self . tcx ) . to_ty ( self . tcx ) . is_region_ptr ( ) {
106+ "`&` reference"
107+ } else {
108+ "`*const` pointer"
109+ } ;
110+ if let Some ( desc) = access_place_desc {
111+ item_msg = format ! ( "`{}`" , desc) ;
112+ reason = match error_access {
113+ AccessKind :: Mutate => format ! ( " which is behind a {}" , pointer_type) ,
114+ AccessKind :: MutableBorrow => {
115+ format ! ( ", as it is behind a {}" , pointer_type)
116+ }
117+ }
118+ } else {
119+ item_msg = format ! ( "data in a {}" , pointer_type) ;
120+ reason = "" . to_string ( ) ;
121+ }
122+ }
123+ }
124+
125+ Place :: Static ( box Static { def_id, ty : _ } ) => {
126+ if let Place :: Static ( _) = access_place {
127+ item_msg = format ! ( "immutable static item `{}`" , access_place_desc. unwrap( ) ) ;
128+ reason = "" . to_string ( ) ;
129+ } else {
130+ item_msg = format ! ( "`{}`" , access_place_desc. unwrap( ) ) ;
131+ let static_name = & self . tcx . item_name ( * def_id) ;
132+ reason = format ! ( ", as `{}` is an immutable static item" , static_name) ;
133+ }
134+ }
135+
136+ Place :: Projection ( box Projection {
137+ base : _,
138+ elem : ProjectionElem :: Index ( _) ,
139+ } )
140+ | Place :: Projection ( box Projection {
141+ base : _,
142+ elem : ProjectionElem :: ConstantIndex { .. } ,
143+ } )
144+ | Place :: Projection ( box Projection {
145+ base : _,
146+ elem : ProjectionElem :: Subslice { .. } ,
147+ } )
148+ | Place :: Projection ( box Projection {
149+ base : _,
150+ elem : ProjectionElem :: Downcast ( ..) ,
151+ } ) => bug ! ( "Unexpected immutable place." ) ,
152+ }
43153
44154 // `act` and `acted_on` are strings that let us abstract over
45155 // the verbs used in some diagnostic messages.
46156 let act;
47157 let acted_on;
48158
49- match error_access {
159+
160+ let span = match error_access {
50161 AccessKind :: Mutate => {
51- let item_msg = match the_place_err {
52- Place :: Projection ( box Projection {
53- base : _,
54- elem : ProjectionElem :: Deref ,
55- } ) => match self . describe_place ( place) {
56- Some ( description) => {
57- format ! ( "`{}` which is behind a `&` reference" , description)
58- }
59- None => format ! ( "data in a `&` reference" ) ,
60- } ,
61- _ => item_msg,
62- } ;
63- err = self . tcx . cannot_assign ( span, & item_msg, Origin :: Mir ) ;
162+ err = self . tcx
163+ . cannot_assign ( span, & ( item_msg + & reason) , Origin :: Mir ) ;
64164 act = "assign" ;
65165 acted_on = "written" ;
166+ span
66167 }
67168 AccessKind :: MutableBorrow => {
68- err = self
69- . tcx
70- . cannot_borrow_path_as_mutable ( span, & item_msg, Origin :: Mir ) ;
71169 act = "borrow as mutable" ;
72170 acted_on = "borrowed as mutable" ;
171+
172+ let closure_span = self . find_closure_span ( span, location) ;
173+ if let Some ( ( args, var) ) = closure_span {
174+ err = self . tcx . cannot_borrow_path_as_mutable_because (
175+ args,
176+ & item_msg,
177+ & reason,
178+ Origin :: Mir ,
179+ ) ;
180+ err. span_label (
181+ var,
182+ format ! (
183+ "mutable borrow occurs due to use of `{}` in closure" ,
184+ self . describe_place( access_place) . unwrap( ) ,
185+ ) ,
186+ ) ;
187+ args
188+ } else {
189+ err = self . tcx . cannot_borrow_path_as_mutable_because (
190+ span,
191+ & item_msg,
192+ & reason,
193+ Origin :: Mir ,
194+ ) ;
195+ span
196+ }
73197 }
74- }
198+ } ;
75199
76200 match the_place_err {
77201 // We want to suggest users use `let mut` for local (user
@@ -80,7 +204,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
80204 // ... but it doesn't make sense to suggest it on
81205 // variables that are `ref x`, `ref mut x`, `&self`,
82206 // or `&mut self` (such variables are simply not
83- // mutable)..
207+ // mutable).
84208 let local_decl = & self . mir . local_decls [ * local] ;
85209 assert_eq ! ( local_decl. mutability, Mutability :: Not ) ;
86210
@@ -92,6 +216,38 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
92216 ) ;
93217 }
94218
219+ // Also suggest adding mut for upvars
220+ Place :: Projection ( box Projection {
221+ base,
222+ elem : ProjectionElem :: Field ( upvar_index, _) ,
223+ } ) => {
224+ debug_assert ! ( is_closure_or_generator(
225+ base. ty( self . mir, self . tcx) . to_ty( self . tcx)
226+ ) ) ;
227+
228+ err. span_label ( span, format ! ( "cannot {ACT}" , ACT = act) ) ;
229+
230+ let upvar_hir_id = self . mir . upvar_decls [ upvar_index. index ( ) ]
231+ . var_hir_id
232+ . assert_crate_local ( ) ;
233+ let upvar_node_id = self . tcx . hir . hir_to_node_id ( upvar_hir_id) ;
234+ if let Some ( hir:: map:: NodeBinding ( pat) ) = self . tcx . hir . find ( upvar_node_id) {
235+ if let hir:: PatKind :: Binding (
236+ hir:: BindingAnnotation :: Unannotated ,
237+ _,
238+ upvar_ident,
239+ _,
240+ ) = pat. node
241+ {
242+ err. span_suggestion (
243+ upvar_ident. span ,
244+ "consider changing this to be mutable" ,
245+ format ! ( "mut {}" , upvar_ident. name) ,
246+ ) ;
247+ }
248+ }
249+ }
250+
95251 // complete hack to approximate old AST-borrowck
96252 // diagnostic: if the span starts with a mutable borrow of
97253 // a local variable, then just suggest the user remove it.
@@ -108,6 +264,25 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
108264 err. span_label ( span, "try removing `&mut` here" ) ;
109265 }
110266
267+ Place :: Projection ( box Projection {
268+ base : Place :: Local ( local) ,
269+ elem : ProjectionElem :: Deref ,
270+ } ) if {
271+ if let Some ( ClearCrossCrate :: Set ( BindingForm :: RefForGuard ) ) =
272+ self . mir . local_decls [ * local] . is_user_variable
273+ {
274+ true
275+ } else {
276+ false
277+ }
278+ } =>
279+ {
280+ err. span_label ( span, format ! ( "cannot {ACT}" , ACT = act) ) ;
281+ err. note (
282+ "variables bound in patterns are immutable until the end of the pattern guard" ,
283+ ) ;
284+ }
285+
111286 // We want to point out when a `&` can be readily replaced
112287 // with an `&mut`.
113288 //
@@ -116,12 +291,13 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
116291 Place :: Projection ( box Projection {
117292 base : Place :: Local ( local) ,
118293 elem : ProjectionElem :: Deref ,
119- } ) if self . mir . local_decls [ * local] . is_user_variable . is_some ( ) => {
294+ } ) if self . mir . local_decls [ * local] . is_user_variable . is_some ( ) =>
295+ {
120296 let local_decl = & self . mir . local_decls [ * local] ;
121297 let suggestion = match local_decl. is_user_variable . as_ref ( ) . unwrap ( ) {
122298 ClearCrossCrate :: Set ( mir:: BindingForm :: ImplicitSelf ) => {
123299 Some ( suggest_ampmut_self ( local_decl) )
124- } ,
300+ }
125301
126302 ClearCrossCrate :: Set ( mir:: BindingForm :: Var ( mir:: VarBindingForm {
127303 binding_mode : ty:: BindingMode :: BindByValue ( _) ,
@@ -140,13 +316,22 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
140316 ..
141317 } ) ) => suggest_ref_mut ( self . tcx , local_decl. source_info . span ) ,
142318
319+ //
320+ ClearCrossCrate :: Set ( mir:: BindingForm :: RefForGuard ) => unreachable ! ( ) ,
321+
143322 ClearCrossCrate :: Clear => bug ! ( "saw cleared local state" ) ,
144323 } ;
145324
325+ let ( pointer_sigil, pointer_desc) = if local_decl. ty . is_region_ptr ( ) {
326+ ( "&" , "reference" )
327+ } else {
328+ ( "*const" , "pointer" )
329+ } ;
330+
146331 if let Some ( ( err_help_span, suggested_code) ) = suggestion {
147332 err. span_suggestion (
148333 err_help_span,
149- "consider changing this to be a mutable reference" ,
334+ & format ! ( "consider changing this to be a mutable {}" , pointer_desc ) ,
150335 suggested_code,
151336 ) ;
152337 }
@@ -155,20 +340,39 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
155340 err. span_label (
156341 span,
157342 format ! (
158- "`{NAME}` is a `&` reference , \
343+ "`{NAME}` is a `{SIGIL}` {DESC} , \
159344 so the data it refers to cannot be {ACTED_ON}",
160345 NAME = name,
346+ SIGIL = pointer_sigil,
347+ DESC = pointer_desc,
161348 ACTED_ON = acted_on
162349 ) ,
163350 ) ;
164351 } else {
165352 err. span_label (
166353 span,
167- format ! ( "cannot {ACT} through `&`-reference" , ACT = act) ,
354+ format ! (
355+ "cannot {ACT} through `{SIGIL}` {DESC}" ,
356+ ACT = act,
357+ SIGIL = pointer_sigil,
358+ DESC = pointer_desc
359+ ) ,
168360 ) ;
169361 }
170362 }
171363
364+ Place :: Projection ( box Projection {
365+ base,
366+ elem : ProjectionElem :: Deref ,
367+ } ) if * base == Place :: Local ( Local :: new ( 1 ) ) && !self . mir . upvar_decls . is_empty ( ) =>
368+ {
369+ err. span_label ( span, format ! ( "cannot {ACT}" , ACT = act) ) ;
370+ err. span_help (
371+ self . mir . span ,
372+ "consider changing this to accept closures that implement `FnMut`"
373+ ) ;
374+ }
375+
172376 _ => {
173377 err. span_label ( span, format ! ( "cannot {ACT}" , ACT = act) ) ;
174378 }
@@ -236,10 +440,7 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
236440 if let Ok ( src) = snippet {
237441 if src. starts_with ( '&' ) {
238442 let borrowed_expr = src[ 1 ..] . to_string ( ) ;
239- return (
240- assignment_rhs_span,
241- format ! ( "&mut {}" , borrowed_expr) ,
242- ) ;
443+ return ( assignment_rhs_span, format ! ( "&mut {}" , borrowed_expr) ) ;
243444 }
244445 }
245446 }
@@ -256,5 +457,13 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
256457
257458 let ty_mut = local_decl. ty . builtin_deref ( true ) . unwrap ( ) ;
258459 assert_eq ! ( ty_mut. mutbl, hir:: MutImmutable ) ;
259- ( highlight_span, format ! ( "&mut {}" , ty_mut. ty) )
460+ if local_decl. ty . is_region_ptr ( ) {
461+ ( highlight_span, format ! ( "&mut {}" , ty_mut. ty) )
462+ } else {
463+ ( highlight_span, format ! ( "*mut {}" , ty_mut. ty) )
464+ }
465+ }
466+
467+ fn is_closure_or_generator ( ty : ty:: Ty ) -> bool {
468+ ty. is_closure ( ) || ty. is_generator ( )
260469}
0 commit comments