1
1
//! This query borrow-checks the MIR to (further) ensure it is not broken.
2
2
3
3
use crate :: borrow_check:: nll:: region_infer:: RegionInferenceContext ;
4
- use rustc:: hir;
4
+ use rustc:: hir:: { self , HirId } ;
5
5
use rustc:: hir:: Node ;
6
6
use rustc:: hir:: def_id:: DefId ;
7
7
use rustc:: infer:: InferCtxt ;
@@ -27,6 +27,7 @@ use std::collections::BTreeMap;
27
27
use std:: mem;
28
28
use std:: rc:: Rc ;
29
29
30
+ use syntax:: ast:: Name ;
30
31
use syntax_pos:: { Span , DUMMY_SP } ;
31
32
32
33
use crate :: dataflow:: indexes:: { BorrowIndex , InitIndex , MoveOutIndex , MovePathIndex } ;
@@ -63,6 +64,19 @@ mod used_muts;
63
64
64
65
pub ( crate ) mod nll;
65
66
67
+ // FIXME(eddyb) perhaps move this somewhere more centrally.
68
+ #[ derive( Debug ) ]
69
+ crate struct Upvar {
70
+ name : Name ,
71
+
72
+ var_hir_id : HirId ,
73
+
74
+ /// If true, the capture is behind a reference.
75
+ by_ref : bool ,
76
+
77
+ mutability : Mutability ,
78
+ }
79
+
66
80
pub fn provide ( providers : & mut Providers < ' _ > ) {
67
81
* providers = Providers {
68
82
mir_borrowck,
@@ -126,6 +140,36 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
126
140
. as_local_hir_id ( def_id)
127
141
. expect ( "do_mir_borrowck: non-local DefId" ) ;
128
142
143
+ // Gather the upvars of a closure, if any.
144
+ let tables = tcx. typeck_tables_of ( def_id) ;
145
+ let upvars: Vec < _ > = tables
146
+ . upvar_list
147
+ . get ( & def_id)
148
+ . into_iter ( )
149
+ . flatten ( )
150
+ . map ( |upvar_id| {
151
+ let var_hir_id = upvar_id. var_path . hir_id ;
152
+ let var_node_id = tcx. hir ( ) . hir_to_node_id ( var_hir_id) ;
153
+ let capture = tables. upvar_capture ( * upvar_id) ;
154
+ let by_ref = match capture {
155
+ ty:: UpvarCapture :: ByValue => false ,
156
+ ty:: UpvarCapture :: ByRef ( ..) => true ,
157
+ } ;
158
+ let mut upvar = Upvar {
159
+ name : tcx. hir ( ) . name ( var_node_id) ,
160
+ var_hir_id,
161
+ by_ref,
162
+ mutability : Mutability :: Not ,
163
+ } ;
164
+ let bm = * tables. pat_binding_modes ( ) . get ( var_hir_id)
165
+ . expect ( "missing binding mode" ) ;
166
+ if bm == ty:: BindByValue ( hir:: MutMutable ) {
167
+ upvar. mutability = Mutability :: Mut ;
168
+ }
169
+ upvar
170
+ } )
171
+ . collect ( ) ;
172
+
129
173
// Replace all regions with fresh inference variables. This
130
174
// requires first making our own copy of the MIR. This copy will
131
175
// be modified (in place) to contain non-lexical lifetimes. It
@@ -168,6 +212,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
168
212
def_id,
169
213
free_regions,
170
214
mir,
215
+ & upvars,
171
216
location_table,
172
217
param_env,
173
218
& mut flow_inits,
@@ -240,6 +285,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
240
285
used_mut_upvars : SmallVec :: new ( ) ,
241
286
borrow_set,
242
287
dominators,
288
+ upvars,
243
289
} ;
244
290
245
291
let mut state = Flows :: new (
@@ -475,6 +521,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
475
521
476
522
/// Dominators for MIR
477
523
dominators : Dominators < BasicBlock > ,
524
+
525
+ /// Information about upvars not necessarily preserved in types or MIR
526
+ upvars : Vec < Upvar > ,
478
527
}
479
528
480
529
// Check that:
@@ -1287,8 +1336,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1287
1336
let propagate_closure_used_mut_place = |this : & mut Self , place : & Place < ' tcx > | {
1288
1337
match * place {
1289
1338
Place :: Projection { .. } => {
1290
- if let Some ( field) = place. is_upvar_field_projection (
1291
- this. mir , & this. infcx . tcx ) {
1339
+ if let Some ( field) = this. is_upvar_field_projection ( place) {
1292
1340
this. used_mut_upvars . push ( field) ;
1293
1341
}
1294
1342
}
@@ -2057,7 +2105,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
2057
2105
place : place @ Place :: Projection ( _) ,
2058
2106
is_local_mutation_allowed : _,
2059
2107
} => {
2060
- if let Some ( field) = place . is_upvar_field_projection ( self . mir , & self . infcx . tcx ) {
2108
+ if let Some ( field) = self . is_upvar_field_projection ( place ) {
2061
2109
self . used_mut_upvars . push ( field) ;
2062
2110
}
2063
2111
}
@@ -2127,13 +2175,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
2127
2175
// Mutably borrowed data is mutable, but only if we have a
2128
2176
// unique path to the `&mut`
2129
2177
hir:: MutMutable => {
2130
- let mode = match place. is_upvar_field_projection (
2131
- self . mir , & self . infcx . tcx )
2132
- {
2178
+ let mode = match self . is_upvar_field_projection ( place) {
2133
2179
Some ( field)
2134
- if {
2135
- self . mir . upvar_decls [ field. index ( ) ] . by_ref
2136
- } =>
2180
+ if self . upvars [ field. index ( ) ] . by_ref =>
2137
2181
{
2138
2182
is_local_mutation_allowed
2139
2183
}
@@ -2173,15 +2217,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
2173
2217
| ProjectionElem :: ConstantIndex { .. }
2174
2218
| ProjectionElem :: Subslice { .. }
2175
2219
| ProjectionElem :: Downcast ( ..) => {
2176
- let upvar_field_projection = place. is_upvar_field_projection (
2177
- self . mir , & self . infcx . tcx ) ;
2220
+ let upvar_field_projection = self . is_upvar_field_projection ( place) ;
2178
2221
if let Some ( field) = upvar_field_projection {
2179
- let decl = & self . mir . upvar_decls [ field. index ( ) ] ;
2222
+ let upvar = & self . upvars [ field. index ( ) ] ;
2180
2223
debug ! (
2181
- "decl .mutability={:?} local_mutation_is_allowed={:?} place={:?}" ,
2182
- decl , is_local_mutation_allowed, place
2224
+ "upvar .mutability={:?} local_mutation_is_allowed={:?} place={:?}" ,
2225
+ upvar , is_local_mutation_allowed, place
2183
2226
) ;
2184
- match ( decl . mutability , is_local_mutation_allowed) {
2227
+ match ( upvar . mutability , is_local_mutation_allowed) {
2185
2228
( Mutability :: Not , LocalMutationIsAllowed :: No )
2186
2229
| ( Mutability :: Not , LocalMutationIsAllowed :: ExceptUpvars ) => {
2187
2230
Err ( place)
@@ -2229,6 +2272,41 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
2229
2272
}
2230
2273
}
2231
2274
}
2275
+
2276
+ /// If `place` is a field projection, and the field is being projected from a closure type,
2277
+ /// then returns the index of the field being projected. Note that this closure will always
2278
+ /// be `self` in the current MIR, because that is the only time we directly access the fields
2279
+ /// of a closure type.
2280
+ pub fn is_upvar_field_projection ( & self , place : & Place < ' tcx > ) -> Option < Field > {
2281
+ let ( place, by_ref) = if let Place :: Projection ( ref proj) = place {
2282
+ if let ProjectionElem :: Deref = proj. elem {
2283
+ ( & proj. base , true )
2284
+ } else {
2285
+ ( place, false )
2286
+ }
2287
+ } else {
2288
+ ( place, false )
2289
+ } ;
2290
+
2291
+ match place {
2292
+ Place :: Projection ( ref proj) => match proj. elem {
2293
+ ProjectionElem :: Field ( field, _ty) => {
2294
+ let tcx = self . infcx . tcx ;
2295
+ let base_ty = proj. base . ty ( self . mir , tcx) . ty ;
2296
+
2297
+ if ( base_ty. is_closure ( ) || base_ty. is_generator ( ) ) &&
2298
+ ( !by_ref || self . upvars [ field. index ( ) ] . by_ref )
2299
+ {
2300
+ Some ( field)
2301
+ } else {
2302
+ None
2303
+ }
2304
+ } ,
2305
+ _ => None ,
2306
+ }
2307
+ _ => None ,
2308
+ }
2309
+ }
2232
2310
}
2233
2311
2234
2312
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
0 commit comments