@@ -49,7 +49,7 @@ use std::{
49
49
use rustc_ast:: Mutability ;
50
50
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
51
51
use rustc_index:: { Idx , IndexVec } ;
52
- use rustc_middle:: mir;
52
+ use rustc_middle:: { mir, ty :: Ty } ;
53
53
use rustc_span:: Span ;
54
54
use rustc_target:: abi:: { Align , HasDataLayout , Size } ;
55
55
@@ -200,18 +200,38 @@ enum AtomicAccessType {
200
200
Rmw ,
201
201
}
202
202
203
- /// Type of write operation: allocating memory
204
- /// non-atomic writes and deallocating memory
205
- /// are all treated as writes for the purpose
206
- /// of the data-race detector.
203
+ /// Type of a non-atomic read operation.
207
204
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
208
- enum NaWriteType {
205
+ pub enum NaReadType {
206
+ /// Standard unsynchronized write.
207
+ Read ,
208
+
209
+ // An implicit read generated by a retag.
210
+ Retag ,
211
+ }
212
+
213
+ impl NaReadType {
214
+ fn description ( self ) -> & ' static str {
215
+ match self {
216
+ NaReadType :: Read => "non-atomic read" ,
217
+ NaReadType :: Retag => "retag read" ,
218
+ }
219
+ }
220
+ }
221
+
222
+ /// Type of a non-atomic write operation: allocating memory, non-atomic writes, and
223
+ /// deallocating memory are all treated as writes for the purpose of the data-race detector.
224
+ #[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
225
+ pub enum NaWriteType {
209
226
/// Allocate memory.
210
227
Allocate ,
211
228
212
229
/// Standard unsynchronized write.
213
230
Write ,
214
231
232
+ // An implicit write generated by a retag.
233
+ Retag ,
234
+
215
235
/// Deallocate memory.
216
236
/// Note that when memory is deallocated first, later non-atomic accesses
217
237
/// will be reported as use-after-free, not as data races.
@@ -224,44 +244,64 @@ impl NaWriteType {
224
244
match self {
225
245
NaWriteType :: Allocate => "creating a new allocation" ,
226
246
NaWriteType :: Write => "non-atomic write" ,
247
+ NaWriteType :: Retag => "retag write" ,
227
248
NaWriteType :: Deallocate => "deallocation" ,
228
249
}
229
250
}
230
251
}
231
252
232
253
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
233
254
enum AccessType {
234
- NaRead ,
255
+ NaRead ( NaReadType ) ,
235
256
NaWrite ( NaWriteType ) ,
236
257
AtomicLoad ,
237
258
AtomicStore ,
238
259
AtomicRmw ,
239
260
}
240
261
241
262
impl AccessType {
242
- fn description ( self ) -> & ' static str {
243
- match self {
244
- AccessType :: NaRead => "non-atomic read" ,
263
+ fn description ( self , ty : Option < Ty < ' _ > > , size : Option < Size > ) -> String {
264
+ let mut msg = String :: new ( ) ;
265
+
266
+ if let Some ( size) = size {
267
+ msg. push_str ( & format ! ( "{}-byte {}" , size. bytes( ) , msg) )
268
+ }
269
+
270
+ msg. push_str ( match self {
271
+ AccessType :: NaRead ( w) => w. description ( ) ,
245
272
AccessType :: NaWrite ( w) => w. description ( ) ,
246
273
AccessType :: AtomicLoad => "atomic load" ,
247
274
AccessType :: AtomicStore => "atomic store" ,
248
275
AccessType :: AtomicRmw => "atomic read-modify-write" ,
276
+ } ) ;
277
+
278
+ if let Some ( ty) = ty {
279
+ msg. push_str ( & format ! ( " of type `{}`" , ty) ) ;
249
280
}
281
+
282
+ msg
250
283
}
251
284
252
285
fn is_atomic ( self ) -> bool {
253
286
match self {
254
287
AccessType :: AtomicLoad | AccessType :: AtomicStore | AccessType :: AtomicRmw => true ,
255
- AccessType :: NaRead | AccessType :: NaWrite ( _) => false ,
288
+ AccessType :: NaRead ( _ ) | AccessType :: NaWrite ( _) => false ,
256
289
}
257
290
}
258
291
259
292
fn is_read ( self ) -> bool {
260
293
match self {
261
- AccessType :: AtomicLoad | AccessType :: NaRead => true ,
294
+ AccessType :: AtomicLoad | AccessType :: NaRead ( _ ) => true ,
262
295
AccessType :: NaWrite ( _) | AccessType :: AtomicStore | AccessType :: AtomicRmw => false ,
263
296
}
264
297
}
298
+
299
+ fn is_retag ( self ) -> bool {
300
+ matches ! (
301
+ self ,
302
+ AccessType :: NaRead ( NaReadType :: Retag ) | AccessType :: NaWrite ( NaWriteType :: Retag )
303
+ )
304
+ }
265
305
}
266
306
267
307
/// Memory Cell vector clock metadata
@@ -502,12 +542,14 @@ impl MemoryCellClocks {
502
542
& mut self ,
503
543
thread_clocks : & mut ThreadClockSet ,
504
544
index : VectorIdx ,
545
+ read_type : NaReadType ,
505
546
current_span : Span ,
506
547
) -> Result < ( ) , DataRace > {
507
548
trace ! ( "Unsynchronized read with vectors: {:#?} :: {:#?}" , self , thread_clocks) ;
508
549
if !current_span. is_dummy ( ) {
509
550
thread_clocks. clock [ index] . span = current_span;
510
551
}
552
+ thread_clocks. clock [ index] . set_read_type ( read_type) ;
511
553
if self . write_was_before ( & thread_clocks. clock ) {
512
554
let race_free = if let Some ( atomic) = self . atomic ( ) {
513
555
// We must be ordered-after all atomic accesses, reads and writes.
@@ -875,7 +917,8 @@ impl VClockAlloc {
875
917
/// This finds the two racing threads and the type
876
918
/// of data-race that occurred. This will also
877
919
/// return info about the memory location the data-race
878
- /// occurred in.
920
+ /// occurred in. The `ty` parameter is used for diagnostics, letting
921
+ /// the user know which type was involved in the access.
879
922
#[ cold]
880
923
#[ inline( never) ]
881
924
fn report_data_race < ' tcx > (
@@ -885,6 +928,7 @@ impl VClockAlloc {
885
928
access : AccessType ,
886
929
access_size : Size ,
887
930
ptr_dbg : Pointer < AllocId > ,
931
+ ty : Option < Ty < ' _ > > ,
888
932
) -> InterpResult < ' tcx > {
889
933
let ( current_index, current_clocks) = global. current_thread_state ( thread_mgr) ;
890
934
let mut other_size = None ; // if `Some`, this was a size-mismatch race
@@ -908,7 +952,7 @@ impl VClockAlloc {
908
952
write_clock = mem_clocks. write ( ) ;
909
953
( AccessType :: NaWrite ( mem_clocks. write_type ) , mem_clocks. write . 0 , & write_clock)
910
954
} else if let Some ( idx) = Self :: find_gt_index ( & mem_clocks. read , & current_clocks. clock ) {
911
- ( AccessType :: NaRead , idx, & mem_clocks. read )
955
+ ( AccessType :: NaRead ( mem_clocks . read [ idx ] . read_type ( ) ) , idx, & mem_clocks. read )
912
956
// Finally, mixed-size races.
913
957
} else if access. is_atomic ( ) && let Some ( atomic) = mem_clocks. atomic ( ) && atomic. size != access_size {
914
958
// This is only a race if we are not synchronized with all atomic accesses, so find
@@ -950,37 +994,33 @@ impl VClockAlloc {
950
994
Err ( err_machine_stop ! ( TerminationInfo :: DataRace {
951
995
involves_non_atomic,
952
996
extra,
997
+ retag_explain: access. is_retag( ) || other_access. is_retag( ) ,
953
998
ptr: ptr_dbg,
954
999
op1: RacingOp {
955
- action: if let Some ( other_size) = other_size {
956
- format!( "{}-byte {}" , other_size. bytes( ) , other_access. description( ) )
957
- } else {
958
- other_access. description( ) . to_owned( )
959
- } ,
1000
+ action: other_access. description( None , other_size) ,
960
1001
thread_info: other_thread_info,
961
1002
span: other_clock. as_slice( ) [ other_thread. index( ) ] . span_data( ) ,
962
1003
} ,
963
1004
op2: RacingOp {
964
- action: if other_size. is_some( ) {
965
- format!( "{}-byte {}" , access_size. bytes( ) , access. description( ) )
966
- } else {
967
- access. description( ) . to_owned( )
968
- } ,
1005
+ action: access. description( ty, other_size. map( |_| access_size) ) ,
969
1006
thread_info: current_thread_info,
970
1007
span: current_clocks. clock. as_slice( ) [ current_index. index( ) ] . span_data( ) ,
971
1008
} ,
972
1009
} ) ) ?
973
1010
}
974
1011
975
- /// Detect data-races for an unsynchronized read operation, will not perform
1012
+ /// Detect data-races for an unsynchronized read operation. It will not perform
976
1013
/// data-race detection if `race_detecting()` is false, either due to no threads
977
1014
/// being created or if it is temporarily disabled during a racy read or write
978
1015
/// operation for which data-race detection is handled separately, for example
979
- /// atomic read operations.
1016
+ /// atomic read operations. The `ty` parameter is used for diagnostics, letting
1017
+ /// the user know which type was read.
980
1018
pub fn read < ' tcx > (
981
1019
& self ,
982
1020
alloc_id : AllocId ,
983
1021
access_range : AllocRange ,
1022
+ read_type : NaReadType ,
1023
+ ty : Option < Ty < ' _ > > ,
984
1024
machine : & MiriMachine < ' _ , ' _ > ,
985
1025
) -> InterpResult < ' tcx > {
986
1026
let current_span = machine. current_span ( ) ;
@@ -992,17 +1032,18 @@ impl VClockAlloc {
992
1032
alloc_ranges. iter_mut ( access_range. start , access_range. size )
993
1033
{
994
1034
if let Err ( DataRace ) =
995
- mem_clocks. read_race_detect ( & mut thread_clocks, index, current_span)
1035
+ mem_clocks. read_race_detect ( & mut thread_clocks, index, read_type , current_span)
996
1036
{
997
1037
drop ( thread_clocks) ;
998
1038
// Report data-race.
999
1039
return Self :: report_data_race (
1000
1040
global,
1001
1041
& machine. threads ,
1002
1042
mem_clocks,
1003
- AccessType :: NaRead ,
1043
+ AccessType :: NaRead ( read_type ) ,
1004
1044
access_range. size ,
1005
1045
Pointer :: new ( alloc_id, Size :: from_bytes ( mem_clocks_range. start ) ) ,
1046
+ ty,
1006
1047
) ;
1007
1048
}
1008
1049
}
@@ -1012,12 +1053,17 @@ impl VClockAlloc {
1012
1053
}
1013
1054
}
1014
1055
1015
- // Shared code for detecting data-races on unique access to a section of memory
1016
- fn unique_access < ' tcx > (
1056
+ /// Detect data-races for an unsynchronized write operation. It will not perform
1057
+ /// data-race detection if `race_detecting()` is false, either due to no threads
1058
+ /// being created or if it is temporarily disabled during a racy read or write
1059
+ /// operation. The `ty` parameter is used for diagnostics, letting
1060
+ /// the user know which type was written.
1061
+ pub fn write < ' tcx > (
1017
1062
& mut self ,
1018
1063
alloc_id : AllocId ,
1019
1064
access_range : AllocRange ,
1020
1065
write_type : NaWriteType ,
1066
+ ty : Option < Ty < ' _ > > ,
1021
1067
machine : & mut MiriMachine < ' _ , ' _ > ,
1022
1068
) -> InterpResult < ' tcx > {
1023
1069
let current_span = machine. current_span ( ) ;
@@ -1042,6 +1088,7 @@ impl VClockAlloc {
1042
1088
AccessType :: NaWrite ( write_type) ,
1043
1089
access_range. size ,
1044
1090
Pointer :: new ( alloc_id, Size :: from_bytes ( mem_clocks_range. start ) ) ,
1091
+ ty,
1045
1092
) ;
1046
1093
}
1047
1094
}
@@ -1050,37 +1097,6 @@ impl VClockAlloc {
1050
1097
Ok ( ( ) )
1051
1098
}
1052
1099
}
1053
-
1054
- /// Detect data-races for an unsynchronized write operation, will not perform
1055
- /// data-race threads if `race_detecting()` is false, either due to no threads
1056
- /// being created or if it is temporarily disabled during a racy read or write
1057
- /// operation
1058
- pub fn write < ' tcx > (
1059
- & mut self ,
1060
- alloc_id : AllocId ,
1061
- range : AllocRange ,
1062
- machine : & mut MiriMachine < ' _ , ' _ > ,
1063
- ) -> InterpResult < ' tcx > {
1064
- self . unique_access ( alloc_id, range, NaWriteType :: Write , machine)
1065
- }
1066
-
1067
- /// Detect data-races for an unsynchronized deallocate operation, will not perform
1068
- /// data-race threads if `race_detecting()` is false, either due to no threads
1069
- /// being created or if it is temporarily disabled during a racy read or write
1070
- /// operation
1071
- pub fn deallocate < ' tcx > (
1072
- & mut self ,
1073
- alloc_id : AllocId ,
1074
- size : Size ,
1075
- machine : & mut MiriMachine < ' _ , ' _ > ,
1076
- ) -> InterpResult < ' tcx > {
1077
- self . unique_access (
1078
- alloc_id,
1079
- alloc_range ( Size :: ZERO , size) ,
1080
- NaWriteType :: Deallocate ,
1081
- machine,
1082
- )
1083
- }
1084
1100
}
1085
1101
1086
1102
impl < ' mir , ' tcx : ' mir > EvalContextPrivExt < ' mir , ' tcx > for MiriInterpCx < ' mir , ' tcx > { }
@@ -1279,7 +1295,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
1279
1295
let alloc_meta = this. get_alloc_extra ( alloc_id) ?. data_race . as_ref ( ) . unwrap ( ) ;
1280
1296
trace ! (
1281
1297
"Atomic op({}) with ordering {:?} on {:?} (size={})" ,
1282
- access. description( ) ,
1298
+ access. description( None , None ) ,
1283
1299
& atomic,
1284
1300
place. ptr( ) ,
1285
1301
size. bytes( )
@@ -1307,6 +1323,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
1307
1323
alloc_id,
1308
1324
Size :: from_bytes ( mem_clocks_range. start ) ,
1309
1325
) ,
1326
+ None ,
1310
1327
)
1311
1328
. map ( |_| true ) ;
1312
1329
}
0 commit comments