@@ -36,42 +36,166 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
36
36
location : Location ,
37
37
) {
38
38
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
+ }
43
153
44
154
// `act` and `acted_on` are strings that let us abstract over
45
155
// the verbs used in some diagnostic messages.
46
156
let act;
47
157
let acted_on;
48
158
49
- match error_access {
159
+
160
+ let span = match error_access {
50
161
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 ) ;
64
164
act = "assign" ;
65
165
acted_on = "written" ;
166
+ span
66
167
}
67
168
AccessKind :: MutableBorrow => {
68
- err = self
69
- . tcx
70
- . cannot_borrow_path_as_mutable ( span, & item_msg, Origin :: Mir ) ;
71
169
act = "borrow as mutable" ;
72
170
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
+ }
73
197
}
74
- }
198
+ } ;
75
199
76
200
match the_place_err {
77
201
// We want to suggest users use `let mut` for local (user
@@ -80,7 +204,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
80
204
// ... but it doesn't make sense to suggest it on
81
205
// variables that are `ref x`, `ref mut x`, `&self`,
82
206
// or `&mut self` (such variables are simply not
83
- // mutable)..
207
+ // mutable).
84
208
let local_decl = & self . mir . local_decls [ * local] ;
85
209
assert_eq ! ( local_decl. mutability, Mutability :: Not ) ;
86
210
@@ -92,6 +216,38 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
92
216
) ;
93
217
}
94
218
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
+
95
251
// complete hack to approximate old AST-borrowck
96
252
// diagnostic: if the span starts with a mutable borrow of
97
253
// a local variable, then just suggest the user remove it.
@@ -108,6 +264,25 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
108
264
err. span_label ( span, "try removing `&mut` here" ) ;
109
265
}
110
266
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
+
111
286
// We want to point out when a `&` can be readily replaced
112
287
// with an `&mut`.
113
288
//
@@ -116,12 +291,13 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
116
291
Place :: Projection ( box Projection {
117
292
base : Place :: Local ( local) ,
118
293
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
+ {
120
296
let local_decl = & self . mir . local_decls [ * local] ;
121
297
let suggestion = match local_decl. is_user_variable . as_ref ( ) . unwrap ( ) {
122
298
ClearCrossCrate :: Set ( mir:: BindingForm :: ImplicitSelf ) => {
123
299
Some ( suggest_ampmut_self ( local_decl) )
124
- } ,
300
+ }
125
301
126
302
ClearCrossCrate :: Set ( mir:: BindingForm :: Var ( mir:: VarBindingForm {
127
303
binding_mode : ty:: BindingMode :: BindByValue ( _) ,
@@ -140,13 +316,22 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
140
316
..
141
317
} ) ) => suggest_ref_mut ( self . tcx , local_decl. source_info . span ) ,
142
318
319
+ //
320
+ ClearCrossCrate :: Set ( mir:: BindingForm :: RefForGuard ) => unreachable ! ( ) ,
321
+
143
322
ClearCrossCrate :: Clear => bug ! ( "saw cleared local state" ) ,
144
323
} ;
145
324
325
+ let ( pointer_sigil, pointer_desc) = if local_decl. ty . is_region_ptr ( ) {
326
+ ( "&" , "reference" )
327
+ } else {
328
+ ( "*const" , "pointer" )
329
+ } ;
330
+
146
331
if let Some ( ( err_help_span, suggested_code) ) = suggestion {
147
332
err. span_suggestion (
148
333
err_help_span,
149
- "consider changing this to be a mutable reference" ,
334
+ & format ! ( "consider changing this to be a mutable {}" , pointer_desc ) ,
150
335
suggested_code,
151
336
) ;
152
337
}
@@ -155,20 +340,39 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
155
340
err. span_label (
156
341
span,
157
342
format ! (
158
- "`{NAME}` is a `&` reference , \
343
+ "`{NAME}` is a `{SIGIL}` {DESC} , \
159
344
so the data it refers to cannot be {ACTED_ON}",
160
345
NAME = name,
346
+ SIGIL = pointer_sigil,
347
+ DESC = pointer_desc,
161
348
ACTED_ON = acted_on
162
349
) ,
163
350
) ;
164
351
} else {
165
352
err. span_label (
166
353
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
+ ) ,
168
360
) ;
169
361
}
170
362
}
171
363
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
+
172
376
_ => {
173
377
err. span_label ( span, format ! ( "cannot {ACT}" , ACT = act) ) ;
174
378
}
@@ -236,10 +440,7 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
236
440
if let Ok ( src) = snippet {
237
441
if src. starts_with ( '&' ) {
238
442
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) ) ;
243
444
}
244
445
}
245
446
}
@@ -256,5 +457,13 @@ fn suggest_ampmut<'cx, 'gcx, 'tcx>(
256
457
257
458
let ty_mut = local_decl. ty . builtin_deref ( true ) . unwrap ( ) ;
258
459
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 ( )
260
469
}
0 commit comments