@@ -9,7 +9,7 @@ use syntax::ast::Mutability;
9
9
use rustc:: middle:: region;
10
10
11
11
use super :: { EvalResult , EvalErrorKind , PrimVal , Pointer , EvalContext , DynamicLifetime , Machine ,
12
- RangeMap } ;
12
+ RangeMap , AbsLvalue } ;
13
13
14
14
////////////////////////////////////////////////////////////////////////////////
15
15
// Locks
@@ -23,14 +23,29 @@ pub enum AccessKind {
23
23
24
24
/// Information about a lock that is currently held.
25
25
#[ derive( Clone , Debug ) ]
26
- struct LockInfo {
26
+ struct LockInfo < ' tcx > {
27
27
/// Stores for which lifetimes (of the original write lock) we got
28
28
/// which suspensions.
29
- suspended : HashMap < DynamicLifetime , Vec < region:: Scope > > ,
29
+ suspended : HashMap < WriteLockId < ' tcx > , Vec < region:: Scope > > ,
30
30
/// The current state of the lock that's actually effective.
31
31
active : Lock ,
32
32
}
33
33
34
+ /// Write locks are identified by a stack frame and an "abstract" (untyped) lvalue.
35
+ /// It may be tempting to use the lifetime as identifier, but that does not work
36
+ /// for two reasons:
37
+ /// * First of all, due to subtyping, the same lock may be referred to with different
38
+ /// lifetimes.
39
+ /// * Secondly, different write locks may actually have the same lifetime. See `test2`
40
+ /// in `run-pass/many_shr_bor.rs`.
41
+ /// The Id is "captured" when the lock is first suspended; at that point, the borrow checker
42
+ /// considers the path frozen and hence the Id remains stable.
43
+ #[ derive( Clone , Debug , PartialEq , Eq , Hash ) ]
44
+ struct WriteLockId < ' tcx > {
45
+ frame : usize ,
46
+ path : AbsLvalue < ' tcx > ,
47
+ }
48
+
34
49
#[ derive( Clone , Debug , PartialEq ) ]
35
50
pub enum Lock {
36
51
NoLock ,
@@ -39,14 +54,14 @@ pub enum Lock {
39
54
}
40
55
use self :: Lock :: * ;
41
56
42
- impl Default for LockInfo {
57
+ impl < ' tcx > Default for LockInfo < ' tcx > {
43
58
fn default ( ) -> Self {
44
59
LockInfo :: new ( NoLock )
45
60
}
46
61
}
47
62
48
- impl LockInfo {
49
- fn new ( lock : Lock ) -> LockInfo {
63
+ impl < ' tcx > LockInfo < ' tcx > {
64
+ fn new ( lock : Lock ) -> LockInfo < ' tcx > {
50
65
LockInfo {
51
66
suspended : HashMap :: new ( ) ,
52
67
active : lock,
@@ -128,7 +143,7 @@ impl fmt::Debug for AllocId {
128
143
}
129
144
130
145
#[ derive( Debug ) ]
131
- pub struct Allocation < M > {
146
+ pub struct Allocation < ' tcx , M > {
132
147
/// The actual bytes of the allocation.
133
148
/// Note that the bytes of a pointer represent the offset of the pointer
134
149
pub bytes : Vec < u8 > ,
@@ -146,17 +161,17 @@ pub struct Allocation<M> {
146
161
/// Helps guarantee that stack allocations aren't deallocated via `rust_deallocate`
147
162
pub kind : MemoryKind < M > ,
148
163
/// Memory regions that are locked by some function
149
- locks : RangeMap < LockInfo > ,
164
+ locks : RangeMap < LockInfo < ' tcx > > ,
150
165
}
151
166
152
- impl < M > Allocation < M > {
153
- fn check_locks < ' tcx > (
167
+ impl < ' tcx , M > Allocation < ' tcx , M > {
168
+ fn check_locks (
154
169
& self ,
155
170
frame : Option < usize > ,
156
171
offset : u64 ,
157
172
len : u64 ,
158
173
access : AccessKind ,
159
- ) -> Result < ( ) , LockInfo > {
174
+ ) -> Result < ( ) , LockInfo < ' tcx > > {
160
175
if len == 0 {
161
176
return Ok ( ( ) ) ;
162
177
}
@@ -237,7 +252,7 @@ pub struct Memory<'a, 'tcx, M: Machine<'tcx>> {
237
252
pub data : M :: MemoryData ,
238
253
239
254
/// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations).
240
- alloc_map : HashMap < u64 , Allocation < M :: MemoryKinds > > ,
255
+ alloc_map : HashMap < u64 , Allocation < ' tcx , M :: MemoryKinds > > ,
241
256
242
257
/// The AllocId to assign to the next new regular allocation. Always incremented, never gets smaller.
243
258
next_alloc_id : u64 ,
@@ -610,62 +625,72 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
610
625
611
626
/// Release or suspend a write lock of the given lifetime prematurely.
612
627
/// When releasing, if there is a read lock or someone else's write lock, that's an error.
613
- /// We *do* accept relasing a NoLock, as this can happen when a local is first acquired and later force_allocate'd.
628
+ /// If no lock is held, that's fine. This can happen when e.g. a local is initialized
629
+ /// from a constant, and then suspended.
614
630
/// When suspending, the same cases are fine; we just register an additional suspension.
615
631
pub ( crate ) fn suspend_write_lock (
616
632
& mut self ,
617
633
ptr : MemoryPointer ,
618
634
len : u64 ,
619
- lock_region : Option < region :: Scope > ,
635
+ lock_path : & AbsLvalue < ' tcx > ,
620
636
suspend : Option < region:: Scope > ,
621
637
) -> EvalResult < ' tcx > {
622
638
assert ! ( len > 0 ) ;
623
639
let cur_frame = self . cur_frame ;
624
- let lock_lft = DynamicLifetime {
625
- frame : cur_frame,
626
- region : lock_region,
627
- } ;
628
640
let alloc = self . get_mut_unchecked ( ptr. alloc_id ) ?;
629
641
630
642
' locks: for lock in alloc. locks . iter_mut ( ptr. offset , len) {
631
643
let is_our_lock = match lock. active {
632
- WriteLock ( lft) => lft == lock_lft,
644
+ WriteLock ( lft) =>
645
+ // Double-check that we are holding the lock.
646
+ // (Due to subtyping, checking the region would not make any sense.)
647
+ lft. frame == cur_frame,
633
648
ReadLock ( _) | NoLock => false ,
634
649
} ;
635
650
if is_our_lock {
636
- trace ! ( "Releasing {:?} at {:?} " , lock. active, lock_lft ) ;
651
+ trace ! ( "Releasing {:?}" , lock. active) ;
637
652
// Disable the lock
638
653
lock. active = NoLock ;
639
654
} else {
640
655
trace ! (
641
- "Not touching {:?} at {:?} as its not our lock" ,
656
+ "Not touching {:?} as it is not our lock" ,
642
657
lock. active,
643
- lock_lft
644
658
) ;
645
659
}
646
- match suspend {
647
- Some ( suspend_region) => {
648
- trace ! ( "Adding suspension to {:?} at {:?}" , lock. active, lock_lft) ;
649
- // We just released this lock, so add a new suspension.
650
- // FIXME: Really, if there ever already is a suspension when is_our_lock, or if there is no suspension when !is_our_lock, something is amiss.
651
- // But this model is not good enough yet to prevent that.
652
- lock. suspended
653
- . entry ( lock_lft)
654
- . or_insert_with ( || Vec :: new ( ) )
655
- . push ( suspend_region) ;
660
+ // Check if we want to register a suspension
661
+ if let Some ( suspend_region) = suspend {
662
+ let lock_id = WriteLockId {
663
+ frame : cur_frame,
664
+ path : lock_path. clone ( ) ,
665
+ } ;
666
+ trace ! ( "Adding suspension to {:?}" , lock_id) ;
667
+ let mut new_suspension = false ;
668
+ lock. suspended
669
+ . entry ( lock_id)
670
+ // Remember whether we added a new suspension or not
671
+ . or_insert_with ( || { new_suspension = true ; Vec :: new ( ) } )
672
+ . push ( suspend_region) ;
673
+ // If the suspension is new, we should have owned this.
674
+ // If there already was a suspension, we should NOT have owned this.
675
+ if new_suspension == is_our_lock {
676
+ // All is well
677
+ continue ' locks;
656
678
}
657
- None => {
658
- // Make sure we did not try to release someone else's lock.
659
- if !is_our_lock && lock. active != NoLock {
660
- return err ! ( InvalidMemoryLockRelease {
661
- ptr,
662
- len,
663
- frame: cur_frame,
664
- lock: lock. active. clone( ) ,
665
- } ) ;
666
- }
679
+ } else {
680
+ if !is_our_lock {
681
+ // All is well.
682
+ continue ' locks;
667
683
}
668
684
}
685
+ // If we get here, releasing this is an error except for NoLock.
686
+ if lock. active != NoLock {
687
+ return err ! ( InvalidMemoryLockRelease {
688
+ ptr,
689
+ len,
690
+ frame: cur_frame,
691
+ lock: lock. active. clone( ) ,
692
+ } ) ;
693
+ }
669
694
}
670
695
671
696
Ok ( ( ) )
@@ -676,26 +701,27 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
676
701
& mut self ,
677
702
ptr : MemoryPointer ,
678
703
len : u64 ,
704
+ lock_path : & AbsLvalue < ' tcx > ,
679
705
lock_region : Option < region:: Scope > ,
680
706
suspended_region : region:: Scope ,
681
707
) -> EvalResult < ' tcx > {
682
708
assert ! ( len > 0 ) ;
683
709
let cur_frame = self . cur_frame ;
684
- let lock_lft = DynamicLifetime {
710
+ let lock_id = WriteLockId {
685
711
frame : cur_frame,
686
- region : lock_region ,
712
+ path : lock_path . clone ( ) ,
687
713
} ;
688
714
let alloc = self . get_mut_unchecked ( ptr. alloc_id ) ?;
689
715
690
716
for lock in alloc. locks . iter_mut ( ptr. offset , len) {
691
717
// Check if we have a suspension here
692
- let ( got_the_lock, remove_suspension) = match lock. suspended . get_mut ( & lock_lft ) {
718
+ let ( got_the_lock, remove_suspension) = match lock. suspended . get_mut ( & lock_id ) {
693
719
None => {
694
720
trace ! ( "No suspension around, we can just acquire" ) ;
695
721
( true , false )
696
722
}
697
723
Some ( suspensions) => {
698
- trace ! ( "Found suspension of {:?}, removing it" , lock_lft ) ;
724
+ trace ! ( "Found suspension of {:?}, removing it" , lock_id ) ;
699
725
// That's us! Remove suspension (it should be in there). The same suspension can
700
726
// occur multiple times (when there are multiple shared borrows of this that have the same
701
727
// lifetime); only remove one of them.
@@ -715,12 +741,17 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
715
741
if remove_suspension {
716
742
// with NLL, we could do that up in the match above...
717
743
assert ! ( got_the_lock) ;
718
- lock. suspended . remove ( & lock_lft ) ;
744
+ lock. suspended . remove ( & lock_id ) ;
719
745
}
720
746
if got_the_lock {
721
747
match lock. active {
722
748
ref mut active @ NoLock => {
723
- * active = WriteLock ( lock_lft) ;
749
+ * active = WriteLock (
750
+ DynamicLifetime {
751
+ frame : cur_frame,
752
+ region : lock_region,
753
+ }
754
+ ) ;
724
755
}
725
756
_ => {
726
757
return err ! ( MemoryAcquireConflict {
@@ -770,8 +801,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
770
801
if lock_ended {
771
802
lock. active = NoLock ;
772
803
}
773
- // Also clean up suspended write locks
774
- lock. suspended . retain ( |lft, _suspensions| !has_ended ( lft) ) ;
804
+ // Also clean up suspended write locks when the function returns
805
+ if ending_region. is_none ( ) {
806
+ lock. suspended . retain ( |id, _suspensions| id. frame != cur_frame) ;
807
+ }
775
808
}
776
809
// Clean up the map
777
810
alloc. locks . retain ( |lock| match lock. active {
@@ -784,7 +817,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
784
817
785
818
/// Allocation accessors
786
819
impl < ' a , ' tcx , M : Machine < ' tcx > > Memory < ' a , ' tcx , M > {
787
- pub fn get ( & self , id : AllocId ) -> EvalResult < ' tcx , & Allocation < M :: MemoryKinds > > {
820
+ pub fn get ( & self , id : AllocId ) -> EvalResult < ' tcx , & Allocation < ' tcx , M :: MemoryKinds > > {
788
821
match id. into_alloc_id_kind ( ) {
789
822
AllocIdKind :: Function ( _) => err ! ( DerefFunctionPointer ) ,
790
823
AllocIdKind :: Runtime ( id) => {
@@ -799,7 +832,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
799
832
fn get_mut_unchecked (
800
833
& mut self ,
801
834
id : AllocId ,
802
- ) -> EvalResult < ' tcx , & mut Allocation < M :: MemoryKinds > > {
835
+ ) -> EvalResult < ' tcx , & mut Allocation < ' tcx , M :: MemoryKinds > > {
803
836
match id. into_alloc_id_kind ( ) {
804
837
AllocIdKind :: Function ( _) => err ! ( DerefFunctionPointer ) ,
805
838
AllocIdKind :: Runtime ( id) => {
@@ -811,7 +844,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
811
844
}
812
845
}
813
846
814
- fn get_mut ( & mut self , id : AllocId ) -> EvalResult < ' tcx , & mut Allocation < M :: MemoryKinds > > {
847
+ fn get_mut ( & mut self , id : AllocId ) -> EvalResult < ' tcx , & mut Allocation < ' tcx , M :: MemoryKinds > > {
815
848
let alloc = self . get_mut_unchecked ( id) ?;
816
849
if alloc. mutable == Mutability :: Mutable {
817
850
Ok ( alloc)
0 commit comments