1
1
use std:: cmp:: { Ordering , PartialOrd } ;
2
2
use std:: fmt;
3
3
4
+ use crate :: borrow_tracker:: tree_borrows:: diagnostics:: TransitionError ;
4
5
use crate :: borrow_tracker:: tree_borrows:: tree:: AccessRelatedness ;
5
6
use crate :: borrow_tracker:: AccessKind ;
6
7
@@ -115,26 +116,31 @@ mod transition {
115
116
/// Public interface to the state machine that controls read-write permissions.
116
117
/// This is the "private `enum`" pattern.
117
118
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
118
- pub struct Permission ( PermissionPriv ) ;
119
+ pub struct Permission {
120
+ inner : PermissionPriv ,
121
+ }
119
122
120
123
/// Transition from one permission to the next.
121
124
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
122
- pub struct PermTransition ( PermissionPriv , PermissionPriv ) ;
125
+ pub struct PermTransition {
126
+ from : PermissionPriv ,
127
+ to : PermissionPriv ,
128
+ }
123
129
124
130
impl Permission {
125
131
/// Default initial permission of the root of a new tree.
126
132
pub fn new_root ( ) -> Self {
127
- Self ( Active )
133
+ Self { inner : Active }
128
134
}
129
135
130
136
/// Default initial permission of a reborrowed mutable reference.
131
137
pub fn new_unique_2phase ( ty_is_freeze : bool ) -> Self {
132
- Self ( Reserved { ty_is_freeze } )
138
+ Self { inner : Reserved { ty_is_freeze } }
133
139
}
134
140
135
141
/// Default initial permission of a reborrowed shared reference
136
142
pub fn new_frozen ( ) -> Self {
137
- Self ( Frozen )
143
+ Self { inner : Frozen }
138
144
}
139
145
140
146
/// Apply the transition to the inner PermissionPriv.
@@ -144,9 +150,9 @@ impl Permission {
144
150
old_perm : Self ,
145
151
protected : bool ,
146
152
) -> Option < PermTransition > {
147
- let old_state = old_perm. 0 ;
153
+ let old_state = old_perm. inner ;
148
154
transition:: perform_access ( kind, rel_pos, old_state, protected)
149
- . map ( |new_state| PermTransition ( old_state, new_state) )
155
+ . map ( |new_state| PermTransition { from : old_state, to : new_state } )
150
156
}
151
157
}
152
158
@@ -155,26 +161,27 @@ impl PermTransition {
155
161
/// should be possible, but the same is not guaranteed by construction of
156
162
/// transitions inferred by diagnostics. This checks that a transition
157
163
/// reconstructed by diagnostics is indeed one that could happen.
158
- fn is_possible ( old : PermissionPriv , new : PermissionPriv ) -> bool {
159
- old <= new
164
+ fn is_possible ( self ) -> bool {
165
+ self . from <= self . to
160
166
}
161
167
162
- pub fn from ( old : Permission , new : Permission ) -> Option < Self > {
163
- Self :: is_possible ( old. 0 , new. 0 ) . then_some ( Self ( old. 0 , new. 0 ) )
168
+ pub fn from ( from : Permission , to : Permission ) -> Option < Self > {
169
+ let t = Self { from : from. inner , to : to. inner } ;
170
+ t. is_possible ( ) . then_some ( t)
164
171
}
165
172
166
173
pub fn is_noop ( self ) -> bool {
167
- self . 0 == self . 1
174
+ self . from == self . to
168
175
}
169
176
170
177
/// Extract result of a transition (checks that the starting point matches).
171
178
pub fn applied ( self , starting_point : Permission ) -> Option < Permission > {
172
- ( starting_point. 0 == self . 0 ) . then_some ( Permission ( self . 1 ) )
179
+ ( starting_point. inner == self . from ) . then_some ( Permission { inner : self . to } )
173
180
}
174
181
175
182
/// Extract starting point of a transition
176
183
pub fn started ( self ) -> Permission {
177
- Permission ( self . 0 )
184
+ Permission { inner : self . from }
178
185
}
179
186
180
187
/// Determines whether a transition that occured is compatible with the presence
@@ -190,10 +197,9 @@ impl PermTransition {
190
197
/// };
191
198
/// ```
192
199
pub fn is_allowed_by_protector ( & self ) -> bool {
193
- let & Self ( old, new) = self ;
194
- assert ! ( Self :: is_possible( old, new) ) ;
195
- match ( old, new) {
196
- _ if old == new => true ,
200
+ assert ! ( self . is_possible( ) ) ;
201
+ match ( self . from , self . to ) {
202
+ _ if self . from == self . to => true ,
197
203
// It is always a protector violation to not be readable anymore
198
204
( _, Disabled ) => false ,
199
205
// In the case of a `Reserved` under a protector, both transitions
@@ -204,16 +210,9 @@ impl PermTransition {
204
210
( Reserved { .. } , Active ) | ( Reserved { .. } , Frozen ) => true ,
205
211
// This pointer should have stayed writeable for the whole function
206
212
( Active , Frozen ) => false ,
207
- _ => unreachable ! ( "Transition from {old:?} to {new:?} should never be possible" ) ,
213
+ _ => unreachable ! ( "Transition {} should never be possible" , self ) ,
208
214
}
209
215
}
210
-
211
- /// Composition function: get the transition that can be added after `app` to
212
- /// produce `self`.
213
- pub fn apply_start ( self , app : Self ) -> Option < Self > {
214
- let new_start = app. applied ( Permission ( self . 0 ) ) ?;
215
- Self :: from ( new_start, Permission ( self . 1 ) )
216
- }
217
216
}
218
217
219
218
pub mod diagnostics {
@@ -224,24 +223,24 @@ pub mod diagnostics {
224
223
f,
225
224
"{}" ,
226
225
match self {
227
- PermissionPriv :: Reserved { .. } => "Reserved" ,
228
- PermissionPriv :: Active => "Active" ,
229
- PermissionPriv :: Frozen => "Frozen" ,
230
- PermissionPriv :: Disabled => "Disabled" ,
226
+ Reserved { .. } => "Reserved" ,
227
+ Active => "Active" ,
228
+ Frozen => "Frozen" ,
229
+ Disabled => "Disabled" ,
231
230
}
232
231
)
233
232
}
234
233
}
235
234
236
235
impl fmt:: Display for PermTransition {
237
236
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
238
- write ! ( f, "from {} to {}" , self . 0 , self . 1 )
237
+ write ! ( f, "from {} to {}" , self . from , self . to )
239
238
}
240
239
}
241
240
242
241
impl fmt:: Display for Permission {
243
242
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
244
- write ! ( f, "{}" , self . 0 )
243
+ write ! ( f, "{}" , self . inner )
245
244
}
246
245
}
247
246
@@ -251,7 +250,7 @@ pub mod diagnostics {
251
250
// Make sure there are all of the same length as each other
252
251
// and also as `diagnostics::DisplayFmtPermission.uninit` otherwise
253
252
// alignment will be incorrect.
254
- match self . 0 {
253
+ match self . inner {
255
254
Reserved { ty_is_freeze : true } => "Res" ,
256
255
Reserved { ty_is_freeze : false } => "Re*" ,
257
256
Active => "Act" ,
@@ -269,16 +268,128 @@ pub mod diagnostics {
269
268
/// to have write permissions, because that's what the diagnostics care about
270
269
/// (otherwise `Reserved -> Frozen` would be considered a noop).
271
270
pub fn summary ( & self ) -> & ' static str {
272
- assert ! ( Self :: is_possible ( self . 0 , self . 1 ) ) ;
273
- match ( self . 0 , self . 1 ) {
274
- ( _, Active ) => "an activation " ,
271
+ assert ! ( self . is_possible ( ) ) ;
272
+ match ( self . from , self . to ) {
273
+ ( _, Active ) => "the first write to a 2-phase borrowed mutable reference " ,
275
274
( _, Frozen ) => "a loss of write permissions" ,
276
275
( Frozen , Disabled ) => "a loss of read permissions" ,
277
276
( _, Disabled ) => "a loss of read and write permissions" ,
278
277
( old, new) =>
279
278
unreachable ! ( "Transition from {old:?} to {new:?} should never be possible" ) ,
280
279
}
281
280
}
281
+
282
+ /// Determines whether `self` is a relevant transition for the error `err`.
283
+ /// `self` will be a transition that happened to a tag some time before
284
+ /// that tag caused the error.
285
+ ///
286
+ /// Irrelevant events:
287
+ /// - modifications of write permissions when the error is related to read permissions
288
+ /// (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Active`,
289
+ /// `Reserved -> Frozen`, and `Active -> Frozen`)
290
+ /// - all transitions for attempts to deallocate strongly protected tags
291
+ ///
292
+ /// # Panics
293
+ ///
294
+ /// This function assumes that its arguments apply to the same location
295
+ /// and that they were obtained during a normal execution. It will panic otherwise.
296
+ /// - `err` cannot be a `ProtectedTransition(_)` of a noop transition, as those
297
+ /// never trigger protectors;
298
+ /// - all transitions involved in `self` and `err` should be increasing
299
+ /// (Reserved < Active < Frozen < Disabled);
300
+ /// - between `self` and `err` the permission should also be increasing,
301
+ /// so all permissions inside `err` should be greater than `self.1`;
302
+ /// - `Active` and `Reserved` cannot cause an error due to insufficient permissions,
303
+ /// so `err` cannot be a `ChildAccessForbidden(_)` of either of them;
304
+ pub ( in super :: super ) fn is_relevant ( & self , err : TransitionError ) -> bool {
305
+ // NOTE: `super::super` is the visibility of `TransitionError`
306
+ assert ! ( self . is_possible( ) ) ;
307
+ if self . is_noop ( ) {
308
+ return false ;
309
+ }
310
+ match err {
311
+ TransitionError :: ChildAccessForbidden ( insufficient) => {
312
+ // Show where the permission was gained then lost,
313
+ // but ignore unrelated permissions.
314
+ // This eliminates transitions like `Active -> Frozen`
315
+ // when the error is a failed `Read`.
316
+ match ( self . to , insufficient. inner ) {
317
+ ( Frozen , Frozen ) => true ,
318
+ ( Active , Frozen ) => true ,
319
+ ( Disabled , Disabled ) => true ,
320
+ // A pointer being `Disabled` is a strictly stronger source of
321
+ // errors than it being `Frozen`. If we try to access a `Disabled`,
322
+ // then where it became `Frozen` (or `Active`) is the least of our concerns for now.
323
+ ( Active | Frozen , Disabled ) => false ,
324
+
325
+ // `Active` and `Reserved` have all permissions, so a
326
+ // `ChildAccessForbidden(Reserved | Active)` can never exist.
327
+ ( _, Active ) | ( _, Reserved { .. } ) =>
328
+ unreachable ! ( "this permission cannot cause an error" ) ,
329
+ // No transition has `Reserved` as its `.to` unless it's a noop.
330
+ ( Reserved { .. } , _) => unreachable ! ( "self is a noop transition" ) ,
331
+ // All transitions produced in normal executions (using `apply_access`)
332
+ // change permissions in the order `Reserved -> Active -> Frozen -> Disabled`.
333
+ // We assume that the error was triggered on the same location that
334
+ // the transition `self` applies to, so permissions found must be increasing
335
+ // in the order `self.from < self.to <= insufficient.inner`
336
+ ( Disabled , Frozen ) =>
337
+ unreachable ! ( "permissions between self and err must be increasing" ) ,
338
+ }
339
+ }
340
+ TransitionError :: ProtectedTransition ( forbidden) => {
341
+ assert ! ( !forbidden. is_noop( ) ) ;
342
+ // Show how we got to the starting point of the forbidden transition,
343
+ // but ignore what came before.
344
+ // This eliminates transitions like `Reserved -> Active`
345
+ // when the error is a `Frozen -> Disabled`.
346
+ match ( self . to , forbidden. from , forbidden. to ) {
347
+ // We absolutely want to know where it was activated.
348
+ ( Active , Active , Frozen | Disabled ) => true ,
349
+ // And knowing where it became Frozen is also important.
350
+ ( Frozen , Frozen , Disabled ) => true ,
351
+ // If the error is a transition `Frozen -> Disabled`, then we don't really
352
+ // care whether before that was `Reserved -> Active -> Frozen` or
353
+ // `Reserved -> Frozen` or even `Frozen` directly.
354
+ // The error will only show either
355
+ // - created as Frozen, then Frozen -> Disabled is forbidden
356
+ // - created as Reserved, later became Frozen, then Frozen -> Disabled is forbidden
357
+ // In both cases the `Reserved -> Active` part is inexistant or irrelevant.
358
+ ( Active , Frozen , Disabled ) => false ,
359
+
360
+ // `Reserved -> Frozen` does not trigger protectors.
361
+ ( _, Reserved { .. } , Frozen ) =>
362
+ unreachable ! ( "this transition cannot cause an error" ) ,
363
+ // No transition has `Reserved` as its `.to` unless it's a noop.
364
+ ( Reserved { .. } , _, _) => unreachable ! ( "self is a noop transition" ) ,
365
+ ( _, Disabled , Disabled ) | ( _, Frozen , Frozen ) | ( _, Active , Active ) =>
366
+ unreachable ! ( "err contains a noop transition" ) ,
367
+
368
+ // Permissions only evolve in the order `Reserved -> Active -> Frozen -> Disabled`,
369
+ // so permissions found must be increasing in the order
370
+ // `self.from < self.to <= forbidden.from < forbidden.to`.
371
+ ( Disabled , Reserved { .. } | Active | Frozen , _)
372
+ | ( Frozen , Reserved { .. } | Active , _)
373
+ | ( Active , Reserved { .. } , _) =>
374
+ unreachable ! ( "permissions between self and err must be increasing" ) ,
375
+ ( _, Disabled , Reserved { .. } | Active | Frozen )
376
+ | ( _, Frozen , Reserved { .. } | Active )
377
+ | ( _, Active , Reserved { .. } ) =>
378
+ unreachable ! ( "permissions within err must be increasing" ) ,
379
+ }
380
+ }
381
+ // We don't care because protectors evolve independently from
382
+ // permissions.
383
+ TransitionError :: ProtectedDealloc => false ,
384
+ }
385
+ }
386
+
387
+ /// Endpoint of a transition.
388
+ /// Meant only for diagnostics, use `applied` in non-diagnostics
389
+ /// code, which also checks that the starting point matches the current state.
390
+ pub fn endpoint ( & self ) -> Permission {
391
+ Permission { inner : self . to }
392
+ }
282
393
}
283
394
}
284
395
0 commit comments