@@ -15,11 +15,9 @@ use rustc::hir;
15
15
use rustc:: mir:: { Mir , Place } ;
16
16
use rustc:: mir:: { Projection , ProjectionElem } ;
17
17
use rustc:: ty:: { self , TyCtxt } ;
18
- use rustc_data_structures:: small_vec:: SmallVec ;
19
- use std:: iter;
20
18
21
- pub ( super ) fn places_conflict < ' a , ' gcx : ' tcx , ' tcx > (
22
- tcx : TyCtxt < ' a , ' gcx , ' tcx > ,
19
+ pub ( super ) fn places_conflict < ' gcx , ' tcx > (
20
+ tcx : TyCtxt < ' _ , ' gcx , ' tcx > ,
23
21
mir : & Mir < ' tcx > ,
24
22
borrow_place : & Place < ' tcx > ,
25
23
access_place : & Place < ' tcx > ,
@@ -30,21 +28,20 @@ pub(super) fn places_conflict<'a, 'gcx: 'tcx, 'tcx>(
30
28
borrow_place, access_place, access
31
29
) ;
32
30
33
- let borrow_components = place_elements ( borrow_place ) ;
34
- let access_components = place_elements ( access_place) ;
35
- debug ! (
36
- "places_conflict: components {:?} / {:?}" ,
37
- borrow_components , access_components
38
- ) ;
31
+ unroll_place ( borrow_place , None , | borrow_components| {
32
+ unroll_place ( access_place, None , |access_components| {
33
+ place_components_conflict ( tcx , mir , borrow_components , access_components , access )
34
+ } )
35
+ } )
36
+ }
39
37
40
- let borrow_components = borrow_components
41
- . into_iter ( )
42
- . map ( Some )
43
- . chain ( iter:: repeat ( None ) ) ;
44
- let access_components = access_components
45
- . into_iter ( )
46
- . map ( Some )
47
- . chain ( iter:: repeat ( None ) ) ;
38
+ fn place_components_conflict < ' gcx , ' tcx > (
39
+ tcx : TyCtxt < ' _ , ' gcx , ' tcx > ,
40
+ mir : & Mir < ' tcx > ,
41
+ borrow_components : PlaceComponentsIter < ' _ , ' tcx > ,
42
+ access_components : PlaceComponentsIter < ' _ , ' tcx > ,
43
+ access : ShallowOrDeep ,
44
+ ) -> bool {
48
45
// The borrowck rules for proving disjointness are applied from the "root" of the
49
46
// borrow forwards, iterating over "similar" projections in lockstep until
50
47
// we can prove overlap one way or another. Essentially, we treat `Overlap` as
@@ -89,24 +86,56 @@ pub(super) fn places_conflict<'a, 'gcx: 'tcx, 'tcx>(
89
86
for ( borrow_c, access_c) in borrow_components. zip ( access_components) {
90
87
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
91
88
debug ! ( "places_conflict: {:?} vs. {:?}" , borrow_c, access_c) ;
92
- match ( borrow_c, access_c) {
93
- ( None , _) => {
94
- // If we didn't run out of access, the borrow can access all of our
95
- // place (e.g. a borrow of `a.b` with an access to `a.b.c`),
96
- // so we have a conflict.
89
+ if let Some ( borrow_c) = borrow_c {
90
+ if let Some ( access_c) = access_c {
91
+ // Borrow and access path both have more components.
97
92
//
98
- // If we did, then we still know that the borrow can access a *part*
99
- // of our place that our access cares about (a borrow of `a.b.c`
100
- // with an access to `a.b`), so we still have a conflict.
93
+ // Examples:
101
94
//
102
- // FIXME: Differs from AST-borrowck; includes drive-by fix
103
- // to #38899. Will probably need back-compat mode flag.
104
- debug ! ( "places_conflict: full borrow, CONFLICT" ) ;
105
- return true ;
106
- }
107
- ( Some ( borrow_c) , None ) => {
108
- // We know that the borrow can access a part of our place. This
109
- // is a conflict if that is a part our access cares about.
95
+ // - borrow of `a.(...)`, access to `a.(...)`
96
+ // - borrow of `a.(...)`, access to `b.(...)`
97
+ //
98
+ // Here we only see the components we have checked so
99
+ // far (in our examples, just the first component). We
100
+ // check whether the components being borrowed vs
101
+ // accessed are disjoint (as in the second example,
102
+ // but not the first).
103
+ match place_element_conflict ( tcx, mir, borrow_c, access_c) {
104
+ Overlap :: Arbitrary => {
105
+ // We have encountered different fields of potentially
106
+ // the same union - the borrow now partially overlaps.
107
+ //
108
+ // There is no *easy* way of comparing the fields
109
+ // further on, because they might have different types
110
+ // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
111
+ // `.y` come from different structs).
112
+ //
113
+ // We could try to do some things here - e.g. count
114
+ // dereferences - but that's probably not a good
115
+ // idea, at least for now, so just give up and
116
+ // report a conflict. This is unsafe code anyway so
117
+ // the user could always use raw pointers.
118
+ debug ! ( "places_conflict: arbitrary -> conflict" ) ;
119
+ return true ;
120
+ }
121
+ Overlap :: EqualOrDisjoint => {
122
+ // This is the recursive case - proceed to the next element.
123
+ }
124
+ Overlap :: Disjoint => {
125
+ // We have proven the borrow disjoint - further
126
+ // projections will remain disjoint.
127
+ debug ! ( "places_conflict: disjoint" ) ;
128
+ return false ;
129
+ }
130
+ }
131
+ } else {
132
+ // Borrow path is longer than the access path. Examples:
133
+ //
134
+ // - borrow of `a.b.c`, access to `a.b`
135
+ //
136
+ // Here, we know that the borrow can access a part of
137
+ // our place. This is a conflict if that is a part our
138
+ // access cares about.
110
139
111
140
let ( base, elem) = match borrow_c {
112
141
Place :: Projection ( box Projection { base, elem } ) => ( base, elem) ,
@@ -164,56 +193,98 @@ pub(super) fn places_conflict<'a, 'gcx: 'tcx, 'tcx>(
164
193
}
165
194
}
166
195
}
167
- ( Some ( borrow_c) , Some ( access_c) ) => {
168
- match place_element_conflict ( tcx, mir, & borrow_c, access_c) {
169
- Overlap :: Arbitrary => {
170
- // We have encountered different fields of potentially
171
- // the same union - the borrow now partially overlaps.
172
- //
173
- // There is no *easy* way of comparing the fields
174
- // further on, because they might have different types
175
- // (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
176
- // `.y` come from different structs).
177
- //
178
- // We could try to do some things here - e.g. count
179
- // dereferences - but that's probably not a good
180
- // idea, at least for now, so just give up and
181
- // report a conflict. This is unsafe code anyway so
182
- // the user could always use raw pointers.
183
- debug ! ( "places_conflict: arbitrary -> conflict" ) ;
184
- return true ;
185
- }
186
- Overlap :: EqualOrDisjoint => {
187
- // This is the recursive case - proceed to the next element.
188
- }
189
- Overlap :: Disjoint => {
190
- // We have proven the borrow disjoint - further
191
- // projections will remain disjoint.
192
- debug ! ( "places_conflict: disjoint" ) ;
193
- return false ;
194
- }
195
- }
196
- }
196
+ } else {
197
+ // Borrow path ran out but access path may not
198
+ // have. Examples:
199
+ //
200
+ // - borrow of `a.b`, access to `a.b.c`
201
+ // - borrow of `a.b`, access to `a.b`
202
+ //
203
+ // In the first example, where we didn't run out of
204
+ // access, the borrow can access all of our place, so we
205
+ // have a conflict.
206
+ //
207
+ // If the second example, where we did, then we still know
208
+ // that the borrow can access a *part* of our place that
209
+ // our access cares about, so we still have a conflict.
210
+ //
211
+ // FIXME: Differs from AST-borrowck; includes drive-by fix
212
+ // to #38899. Will probably need back-compat mode flag.
213
+ debug ! ( "places_conflict: full borrow, CONFLICT" ) ;
214
+ return true ;
197
215
}
198
216
}
199
217
unreachable ! ( "iter::repeat returned None" )
200
218
}
201
219
202
- /// Return all the prefixes of `place` in reverse order, including
203
- /// downcasts.
204
- fn place_elements < ' a , ' tcx > ( place : & ' a Place < ' tcx > ) -> SmallVec < [ & ' a Place < ' tcx > ; 8 ] > {
205
- let mut result = SmallVec :: new ( ) ;
206
- let mut place = place;
207
- loop {
208
- result. push ( place) ;
209
- match place {
210
- Place :: Projection ( interior) => {
211
- place = & interior. base ;
212
- }
213
- Place :: Local ( _) | Place :: Static ( _) => {
214
- result. reverse ( ) ;
215
- return result;
216
- }
220
+ /// A linked list of places running up the stack; begins with the
221
+ /// innermost place and extends to projections (e.g., `a.b` would have
222
+ /// the place `a` with a "next" pointer to `a.b`). Created by
223
+ /// `unroll_place`.
224
+ struct PlaceComponents < ' p , ' tcx : ' p > {
225
+ component : & ' p Place < ' tcx > ,
226
+ next : Option < & ' p PlaceComponents < ' p , ' tcx > > ,
227
+ }
228
+
229
+ impl < ' p , ' tcx > PlaceComponents < ' p , ' tcx > {
230
+ /// Converts a list of `Place` components into an iterator; this
231
+ /// iterator yields up a never-ending stream of `Option<&Place>`.
232
+ /// These begin with the "innermst" place and then with each
233
+ /// projection therefrom. So given a place like `a.b.c` it would
234
+ /// yield up:
235
+ ///
236
+ /// ```notrust
237
+ /// Some(`a`), Some(`a.b`), Some(`a.b.c`), None, None, ...
238
+ /// ```
239
+ fn iter ( & self ) -> PlaceComponentsIter < ' _ , ' tcx > {
240
+ PlaceComponentsIter { value : Some ( self ) }
241
+ }
242
+ }
243
+
244
+ /// Iterator over components; see `PlaceComponents::iter` for more
245
+ /// information.
246
+ struct PlaceComponentsIter < ' p , ' tcx : ' p > {
247
+ value : Option < & ' p PlaceComponents < ' p , ' tcx > >
248
+ }
249
+
250
+ impl < ' p , ' tcx > Iterator for PlaceComponentsIter < ' p , ' tcx > {
251
+ type Item = Option < & ' p Place < ' tcx > > ;
252
+
253
+ fn next ( & mut self ) -> Option < Self :: Item > {
254
+ if let Some ( & PlaceComponents { component, next } ) = self . value {
255
+ self . value = next;
256
+ Some ( Some ( component) )
257
+ } else {
258
+ Some ( None )
259
+ }
260
+ }
261
+ }
262
+
263
+ /// Recursively "unroll" a place into a `PlaceComponents` list,
264
+ /// invoking `op` with a `PlaceComponentsIter`.
265
+ fn unroll_place < ' tcx , R > (
266
+ place : & Place < ' tcx > ,
267
+ next : Option < & PlaceComponents < ' _ , ' tcx > > ,
268
+ op : impl FnOnce ( PlaceComponentsIter < ' _ , ' tcx > ) -> R
269
+ ) -> R {
270
+ match place {
271
+ Place :: Projection ( interior) => {
272
+ unroll_place (
273
+ & interior. base ,
274
+ Some ( & PlaceComponents {
275
+ component : place,
276
+ next,
277
+ } ) ,
278
+ op,
279
+ )
280
+ }
281
+
282
+ Place :: Local ( _) | Place :: Static ( _) => {
283
+ let list = PlaceComponents {
284
+ component : place,
285
+ next,
286
+ } ;
287
+ op ( list. iter ( ) )
217
288
}
218
289
}
219
290
}
0 commit comments