11//! This query borrow-checks the MIR to (further) ensure it is not broken.
22
33use crate :: borrow_check:: nll:: region_infer:: RegionInferenceContext ;
4- use rustc:: hir;
4+ use rustc:: hir:: { self , HirId } ;
55use rustc:: hir:: Node ;
66use rustc:: hir:: def_id:: DefId ;
77use rustc:: infer:: InferCtxt ;
@@ -27,6 +27,7 @@ use std::collections::BTreeMap;
2727use std:: mem;
2828use std:: rc:: Rc ;
2929
30+ use syntax:: ast:: Name ;
3031use syntax_pos:: { Span , DUMMY_SP } ;
3132
3233use crate :: dataflow:: indexes:: { BorrowIndex , InitIndex , MoveOutIndex , MovePathIndex } ;
@@ -63,6 +64,19 @@ mod used_muts;
6364
6465pub ( crate ) mod nll;
6566
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+
6680pub fn provide ( providers : & mut Providers < ' _ > ) {
6781 * providers = Providers {
6882 mir_borrowck,
@@ -126,6 +140,36 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
126140 . as_local_hir_id ( def_id)
127141 . expect ( "do_mir_borrowck: non-local DefId" ) ;
128142
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+
129173 // Replace all regions with fresh inference variables. This
130174 // requires first making our own copy of the MIR. This copy will
131175 // be modified (in place) to contain non-lexical lifetimes. It
@@ -168,6 +212,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
168212 def_id,
169213 free_regions,
170214 mir,
215+ & upvars,
171216 location_table,
172217 param_env,
173218 & mut flow_inits,
@@ -240,6 +285,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
240285 used_mut_upvars : SmallVec :: new ( ) ,
241286 borrow_set,
242287 dominators,
288+ upvars,
243289 } ;
244290
245291 let mut state = Flows :: new (
@@ -475,6 +521,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
475521
476522 /// Dominators for MIR
477523 dominators : Dominators < BasicBlock > ,
524+
525+ /// Information about upvars not necessarily preserved in types or MIR
526+ upvars : Vec < Upvar > ,
478527}
479528
480529// Check that:
@@ -1287,8 +1336,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12871336 let propagate_closure_used_mut_place = |this : & mut Self , place : & Place < ' tcx > | {
12881337 match * place {
12891338 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) {
12921340 this. used_mut_upvars . push ( field) ;
12931341 }
12941342 }
@@ -2057,7 +2105,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
20572105 place : place @ Place :: Projection ( _) ,
20582106 is_local_mutation_allowed : _,
20592107 } => {
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 ) {
20612109 self . used_mut_upvars . push ( field) ;
20622110 }
20632111 }
@@ -2127,13 +2175,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
21272175 // Mutably borrowed data is mutable, but only if we have a
21282176 // unique path to the `&mut`
21292177 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) {
21332179 Some ( field)
2134- if {
2135- self . mir . upvar_decls [ field. index ( ) ] . by_ref
2136- } =>
2180+ if self . upvars [ field. index ( ) ] . by_ref =>
21372181 {
21382182 is_local_mutation_allowed
21392183 }
@@ -2173,15 +2217,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
21732217 | ProjectionElem :: ConstantIndex { .. }
21742218 | ProjectionElem :: Subslice { .. }
21752219 | 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) ;
21782221 if let Some ( field) = upvar_field_projection {
2179- let decl = & self . mir . upvar_decls [ field. index ( ) ] ;
2222+ let upvar = & self . upvars [ field. index ( ) ] ;
21802223 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
21832226 ) ;
2184- match ( decl . mutability , is_local_mutation_allowed) {
2227+ match ( upvar . mutability , is_local_mutation_allowed) {
21852228 ( Mutability :: Not , LocalMutationIsAllowed :: No )
21862229 | ( Mutability :: Not , LocalMutationIsAllowed :: ExceptUpvars ) => {
21872230 Err ( place)
@@ -2229,6 +2272,41 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
22292272 }
22302273 }
22312274 }
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+ }
22322310}
22332311
22342312#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
0 commit comments