11use std:: cmp:: { Ordering , PartialOrd } ;
22use std:: fmt;
33
4+ use crate :: borrow_tracker:: tree_borrows:: diagnostics:: TransitionError ;
45use crate :: borrow_tracker:: tree_borrows:: tree:: AccessRelatedness ;
56use crate :: borrow_tracker:: AccessKind ;
67
@@ -115,26 +116,31 @@ mod transition {
115116/// Public interface to the state machine that controls read-write permissions.
116117/// This is the "private `enum`" pattern.
117118#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
118- pub struct Permission ( PermissionPriv ) ;
119+ pub struct Permission {
120+ inner : PermissionPriv ,
121+ }
119122
120123/// Transition from one permission to the next.
121124#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
122- pub struct PermTransition ( PermissionPriv , PermissionPriv ) ;
125+ pub struct PermTransition {
126+ from : PermissionPriv ,
127+ to : PermissionPriv ,
128+ }
123129
124130impl Permission {
125131 /// Default initial permission of the root of a new tree.
126132 pub fn new_root ( ) -> Self {
127- Self ( Active )
133+ Self { inner : Active }
128134 }
129135
130136 /// Default initial permission of a reborrowed mutable reference.
131137 pub fn new_unique_2phase ( ty_is_freeze : bool ) -> Self {
132- Self ( Reserved { ty_is_freeze } )
138+ Self { inner : Reserved { ty_is_freeze } }
133139 }
134140
135141 /// Default initial permission of a reborrowed shared reference
136142 pub fn new_frozen ( ) -> Self {
137- Self ( Frozen )
143+ Self { inner : Frozen }
138144 }
139145
140146 /// Apply the transition to the inner PermissionPriv.
@@ -144,9 +150,9 @@ impl Permission {
144150 old_perm : Self ,
145151 protected : bool ,
146152 ) -> Option < PermTransition > {
147- let old_state = old_perm. 0 ;
153+ let old_state = old_perm. inner ;
148154 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 } )
150156 }
151157}
152158
@@ -155,26 +161,27 @@ impl PermTransition {
155161 /// should be possible, but the same is not guaranteed by construction of
156162 /// transitions inferred by diagnostics. This checks that a transition
157163 /// 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
160166 }
161167
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)
164171 }
165172
166173 pub fn is_noop ( self ) -> bool {
167- self . 0 == self . 1
174+ self . from == self . to
168175 }
169176
170177 /// Extract result of a transition (checks that the starting point matches).
171178 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 } )
173180 }
174181
175182 /// Extract starting point of a transition
176183 pub fn started ( self ) -> Permission {
177- Permission ( self . 0 )
184+ Permission { inner : self . from }
178185 }
179186
180187 /// Determines whether a transition that occured is compatible with the presence
@@ -190,10 +197,9 @@ impl PermTransition {
190197 /// };
191198 /// ```
192199 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 ,
197203 // It is always a protector violation to not be readable anymore
198204 ( _, Disabled ) => false ,
199205 // In the case of a `Reserved` under a protector, both transitions
@@ -204,16 +210,9 @@ impl PermTransition {
204210 ( Reserved { .. } , Active ) | ( Reserved { .. } , Frozen ) => true ,
205211 // This pointer should have stayed writeable for the whole function
206212 ( Active , Frozen ) => false ,
207- _ => unreachable ! ( "Transition from {old:?} to {new:?} should never be possible" ) ,
213+ _ => unreachable ! ( "Transition {} should never be possible" , self ) ,
208214 }
209215 }
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- }
217216}
218217
219218pub mod diagnostics {
@@ -224,24 +223,24 @@ pub mod diagnostics {
224223 f,
225224 "{}" ,
226225 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" ,
231230 }
232231 )
233232 }
234233 }
235234
236235 impl fmt:: Display for PermTransition {
237236 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 )
239238 }
240239 }
241240
242241 impl fmt:: Display for Permission {
243242 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
244- write ! ( f, "{}" , self . 0 )
243+ write ! ( f, "{}" , self . inner )
245244 }
246245 }
247246
@@ -251,7 +250,7 @@ pub mod diagnostics {
251250 // Make sure there are all of the same length as each other
252251 // and also as `diagnostics::DisplayFmtPermission.uninit` otherwise
253252 // alignment will be incorrect.
254- match self . 0 {
253+ match self . inner {
255254 Reserved { ty_is_freeze : true } => "Res" ,
256255 Reserved { ty_is_freeze : false } => "Re*" ,
257256 Active => "Act" ,
@@ -269,16 +268,128 @@ pub mod diagnostics {
269268 /// to have write permissions, because that's what the diagnostics care about
270269 /// (otherwise `Reserved -> Frozen` would be considered a noop).
271270 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 " ,
275274 ( _, Frozen ) => "a loss of write permissions" ,
276275 ( Frozen , Disabled ) => "a loss of read permissions" ,
277276 ( _, Disabled ) => "a loss of read and write permissions" ,
278277 ( old, new) =>
279278 unreachable ! ( "Transition from {old:?} to {new:?} should never be possible" ) ,
280279 }
281280 }
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+ }
282393 }
283394}
284395
0 commit comments