@@ -3,7 +3,6 @@ use std::ops::Range;
3
3
4
4
use rustc_data_structures:: fx:: FxHashMap ;
5
5
use rustc_span:: { Span , SpanData } ;
6
- use rustc_target:: abi:: Size ;
7
6
8
7
use crate :: borrow_tracker:: tree_borrows:: {
9
8
perms:: { PermTransition , Permission } ,
@@ -14,18 +13,30 @@ use crate::borrow_tracker::{AccessKind, ProtectorKind};
14
13
use crate :: * ;
15
14
16
15
/// Complete data for an event:
17
- /// - `kind` is what happened to the permissions
18
- /// - `access_kind` and `access_range` describe the access that caused the event
19
- /// - `offset` allows filtering only the relevant events for a given memory location
20
- /// (see how we perform the filtering in `History::extract_relevant`.
21
- /// - `span` is the line of code in question
22
16
#[ derive( Clone , Debug ) ]
23
17
pub struct Event {
18
+ /// Transformation of permissions that occured because of this event
24
19
pub transition : PermTransition ,
20
+ /// Kind of the access that triggered this event
25
21
pub access_kind : AccessKind ,
22
+ /// Relative position of the tag to the one used for the access
26
23
pub is_foreign : bool ,
24
+ /// User-visible range of the access
27
25
pub access_range : AllocRange ,
28
- pub offset : Size ,
26
+ /// The transition recorded by this event only occured on a subrange of
27
+ /// `access_range`: a single access on `access_range` triggers several events,
28
+ /// each with their own mutually disjoint `transition_range`. No-op transitions
29
+ /// should not be recorded as events, so the union of all `transition_range` is not
30
+ /// necessarily the entire `access_range`.
31
+ ///
32
+ /// No data from any `transition_range` should ever be user-visible, because
33
+ /// both the start and end of `transition_range` are entirely dependent on the
34
+ /// internal representation of `RangeMap` which is supposed to be opaque.
35
+ /// What will be shown in the error message is the first byte `error_offset` of
36
+ /// the `TbError`, which should satisfy
37
+ /// `event.transition_range.contains(error.error_offset)`.
38
+ pub transition_range : Range < u64 > ,
39
+ /// Line of code that triggered this event
29
40
pub span : Span ,
30
41
}
31
42
@@ -35,9 +46,9 @@ pub struct Event {
35
46
/// Available filtering methods include `History::forget` and `History::extract_relevant`.
36
47
#[ derive( Clone , Debug ) ]
37
48
pub struct History {
38
- pub tag : BorTag ,
39
- pub created : ( Span , Permission ) ,
40
- pub events : Vec < Event > ,
49
+ tag : BorTag ,
50
+ created : ( Span , Permission ) ,
51
+ events : Vec < Event > ,
41
52
}
42
53
43
54
/// History formatted for use by `src/diagnostics.rs`.
@@ -60,12 +71,7 @@ impl HistoryData {
60
71
// Format events from `new_history` into those recorded by `self`.
61
72
//
62
73
// NOTE: also converts `Span` to `SpanData`.
63
- pub fn extend (
64
- & mut self ,
65
- new_history : History ,
66
- tag_name : & ' static str ,
67
- show_initial_state : bool ,
68
- ) {
74
+ fn extend ( & mut self , new_history : History , tag_name : & ' static str , show_initial_state : bool ) {
69
75
let History { tag, created, events } = new_history;
70
76
let this = format ! ( "the {tag_name} tag {tag:?}" ) ;
71
77
let msg_initial_state = format ! ( ", in the initial state {}" , created. 1 ) ;
@@ -75,9 +81,16 @@ impl HistoryData {
75
81
) ;
76
82
77
83
self . events . push ( ( Some ( created. 0 . data ( ) ) , msg_creation) ) ;
78
- for & Event { transition, access_kind, is_foreign, access_range, span, offset : _ } in & events
84
+ for & Event {
85
+ transition,
86
+ access_kind,
87
+ is_foreign,
88
+ access_range,
89
+ span,
90
+ transition_range : _,
91
+ } in & events
79
92
{
80
- // NOTE: `offset ` is explicitly absent from the error message, it has no significance
93
+ // NOTE: `transition_range ` is explicitly absent from the error message, it has no significance
81
94
// to the user. The meaningful one is `access_range`.
82
95
self . events . push ( ( Some ( span. data ( ) ) , format ! ( "{this} then transitioned {transition} due to a {rel} {access_kind} at offsets {access_range:?}" , rel = if is_foreign { "foreign" } else { "child" } ) ) ) ;
83
96
self . events . push ( ( None , format ! ( "this corresponds to {}" , transition. summary( ) ) ) ) ;
@@ -197,53 +210,28 @@ impl History {
197
210
History { events : Vec :: new ( ) , created : self . created , tag : self . tag }
198
211
}
199
212
200
- /// Reconstruct the history relevant to `error_offset` knowing that
201
- /// its permission followed `complete_transition`.
202
- ///
203
- /// Here's how we do this:
204
- /// - we know `full := complete_transition` the transition of the permission from
205
- /// its initialization to the state just before the error was caused,
206
- /// we want to find a chain of events that produces `full`
207
- /// - we decompose `full` into `pre o post` where
208
- /// `pre` is the best applicable transition from recorded events
209
- /// - we select the event that caused `pre` and iterate
210
- /// to find the chain of events that produces `full := post`
211
- ///
212
- /// To find the "best applicable transition" for full:
213
- /// - eliminate events that cannot be applied because their offset is too big
214
- /// - eliminate events that cannot be applied because their starting point is wrong
215
- /// - select the one that happened closest to the range of interest
216
- fn extract_relevant ( & self , complete_transition : PermTransition , error_offset : Size ) -> Self {
217
- let mut selected_events: Vec < Event > = Vec :: new ( ) ;
218
- let mut full = complete_transition;
219
- while !full. is_noop ( ) {
220
- let ( pre, post) = self
213
+ /// Reconstruct the history relevant to `error_offset` by filtering
214
+ /// only events whose range contains the offset we are interested in.
215
+ fn extract_relevant ( & self , error_offset : u64 ) -> Self {
216
+ History {
217
+ events : self
221
218
. events
222
219
. iter ( )
223
- . filter ( |e| e. offset <= error_offset)
224
- . filter_map ( |pre_canditate| {
225
- full. apply_start ( pre_canditate. transition )
226
- . map ( |post_canditate| ( pre_canditate, post_canditate) )
227
- } )
228
- . max_by_key ( |( pre_canditate, _post_candidate) | pre_canditate. offset )
229
- . unwrap ( ) ;
230
- // If this occurs we will loop infinitely !
231
- // Make sure to only put non-noop transitions in `History`.
232
- assert ! ( !pre. transition. is_noop( ) ) ;
233
- full = post;
234
- selected_events. push ( pre. clone ( ) ) ;
220
+ . filter ( |e| e. transition_range . contains ( & error_offset) )
221
+ . cloned ( )
222
+ . collect :: < Vec < _ > > ( ) ,
223
+ created : self . created ,
224
+ tag : self . tag ,
235
225
}
236
-
237
- History { events : selected_events, created : self . created , tag : self . tag }
238
226
}
239
227
}
240
228
241
229
/// Failures that can occur during the execution of Tree Borrows procedures.
242
230
pub ( super ) struct TbError < ' node > {
243
231
/// What failure occurred.
244
232
pub error_kind : TransitionError ,
245
- /// The byte at which the conflict occured .
246
- pub error_offset : Size ,
233
+ /// The offset (into the allocation) at which the conflict occurred .
234
+ pub error_offset : u64 ,
247
235
/// The tag on which the error was triggered.
248
236
/// On protector violations, this is the tag that was protected.
249
237
/// On accesses rejected due to insufficient permissions, this is the
@@ -261,12 +249,11 @@ impl TbError<'_> {
261
249
/// Produce a UB error.
262
250
pub fn build < ' tcx > ( self ) -> InterpError < ' tcx > {
263
251
use TransitionError :: * ;
264
- let started_as = self . conflicting_info . history . created . 1 ;
265
252
let kind = self . access_kind ;
266
253
let accessed = self . accessed_info ;
267
254
let conflicting = self . conflicting_info ;
268
255
let accessed_is_conflicting = accessed. tag == conflicting. tag ;
269
- let ( pre_error , title, details, conflicting_tag_name) = match self . error_kind {
256
+ let ( title, details, conflicting_tag_name) = match self . error_kind {
270
257
ChildAccessForbidden ( perm) => {
271
258
let conflicting_tag_name =
272
259
if accessed_is_conflicting { "accessed" } else { "conflicting" } ;
@@ -280,7 +267,7 @@ impl TbError<'_> {
280
267
details. push ( format ! (
281
268
"the {conflicting_tag_name} tag {conflicting} has state {perm} which forbids child {kind}es"
282
269
) ) ;
283
- ( perm , title, details, conflicting_tag_name)
270
+ ( title, details, conflicting_tag_name)
284
271
}
285
272
ProtectedTransition ( transition) => {
286
273
let conflicting_tag_name = "protected" ;
@@ -297,7 +284,7 @@ impl TbError<'_> {
297
284
loss = transition. summary( ) ,
298
285
) ,
299
286
] ;
300
- ( transition . started ( ) , title, details, conflicting_tag_name)
287
+ ( title, details, conflicting_tag_name)
301
288
}
302
289
ProtectedDealloc => {
303
290
let conflicting_tag_name = "strongly protected" ;
@@ -308,16 +295,15 @@ impl TbError<'_> {
308
295
) ,
309
296
format!( "the {conflicting_tag_name} tag {conflicting} disallows deallocations" ) ,
310
297
] ;
311
- ( started_as , title, details, conflicting_tag_name)
298
+ ( title, details, conflicting_tag_name)
312
299
}
313
300
} ;
314
- let pre_transition = PermTransition :: from ( started_as, pre_error) . unwrap ( ) ;
315
301
let mut history = HistoryData :: default ( ) ;
316
302
if !accessed_is_conflicting {
317
303
history. extend ( self . accessed_info . history . forget ( ) , "accessed" , false ) ;
318
304
}
319
305
history. extend (
320
- self . conflicting_info . history . extract_relevant ( pre_transition , self . error_offset ) ,
306
+ self . conflicting_info . history . extract_relevant ( self . error_offset ) ,
321
307
conflicting_tag_name,
322
308
true ,
323
309
) ;
0 commit comments