@@ -12,6 +12,7 @@ use middle::infer::InferCtxt;
12
12
use middle:: ty:: { self , RegionEscape , Ty } ;
13
13
use std:: collections:: HashSet ;
14
14
use std:: default:: Default ;
15
+ use std:: mem;
15
16
use syntax:: ast;
16
17
use util:: common:: ErrorReported ;
17
18
use util:: ppaux:: Repr ;
@@ -80,6 +81,11 @@ pub struct FulfillmentContext<'tcx> {
80
81
// obligations (otherwise, it's easy to fail to walk to a
81
82
// particular node-id).
82
83
region_obligations : NodeMap < Vec < RegionObligation < ' tcx > > > ,
84
+
85
+ // Stored versions of the fields for snapshots
86
+ stored_region_obligations : Option < NodeMap < Vec < RegionObligation < ' tcx > > > > ,
87
+ stored_predicates : Option < Vec < PredicateObligation < ' tcx > > > ,
88
+ stored_duplicate_set : Option < HashSet < ty:: Predicate < ' tcx > > >
83
89
}
84
90
85
91
#[ derive( Clone ) ]
@@ -96,6 +102,51 @@ impl<'tcx> FulfillmentContext<'tcx> {
96
102
predicates : Vec :: new ( ) ,
97
103
attempted_mark : 0 ,
98
104
region_obligations : NodeMap ( ) ,
105
+
106
+ stored_region_obligations : None ,
107
+ stored_predicates : None ,
108
+ stored_duplicate_set : None
109
+ }
110
+ }
111
+
112
+ /// Begin a snapshot. This should be done in parallel with infcx
113
+ /// snapshots.
114
+ pub fn begin_transaction ( & mut self ) {
115
+ assert ! ( self . stored_duplicate_set. is_none( ) , "nested transactions are not supported" ) ;
116
+ debug ! ( "begin_transaction" ) ;
117
+
118
+ self . stored_duplicate_set = Some ( mem:: replace ( & mut self . duplicate_set ,
119
+ HashSet :: new ( ) ) ) ;
120
+ self . stored_predicates = Some ( self . predicates . clone ( ) ) ;
121
+ self . stored_region_obligations = Some ( mem:: replace ( & mut self . region_obligations ,
122
+ NodeMap ( ) ) ) ;
123
+ }
124
+
125
+ /// Rolls the current transaction back
126
+ pub fn rollback ( & mut self ) {
127
+ assert ! ( self . stored_duplicate_set. is_some( ) , "rollback not within a transaction" ) ;
128
+ debug ! ( "rollback!" ) ;
129
+
130
+ self . duplicate_set = self . stored_duplicate_set . take ( ) . unwrap ( ) ;
131
+ self . predicates = self . stored_predicates . take ( ) . unwrap ( ) ;
132
+ self . region_obligations = self . stored_region_obligations . take ( ) . unwrap ( ) ;
133
+ }
134
+
135
+ /// Commits the current transaction
136
+ pub fn commit ( & mut self ) {
137
+ assert ! ( self . stored_duplicate_set. is_some( ) , "commit not within a transaction" ) ;
138
+ debug ! ( "commit!" ) ;
139
+
140
+ let transaction_duplicate_set = mem:: replace ( & mut self . duplicate_set ,
141
+ self . stored_duplicate_set . take ( ) . unwrap ( ) ) ;
142
+ let transaction_region_obligations = mem:: replace ( & mut self . region_obligations ,
143
+ self . stored_region_obligations . take ( ) . unwrap ( ) ) ;
144
+
145
+ self . duplicate_set . extend ( transaction_duplicate_set) ;
146
+ self . predicates = self . stored_predicates . take ( ) . unwrap ( ) ;
147
+
148
+ for ( node, mut ros) in transaction_region_obligations {
149
+ self . region_obligations . entry ( node) . or_insert ( vec ! [ ] ) . append ( & mut ros) ;
99
150
}
100
151
}
101
152
@@ -170,6 +221,14 @@ impl<'tcx> FulfillmentContext<'tcx> {
170
221
return ;
171
222
}
172
223
224
+ if let Some ( ref duplicate_set) = self . stored_duplicate_set {
225
+ if duplicate_set. contains ( & obligation. predicate ) {
226
+ debug ! ( "register_predicate({}) -- already seen before transaction, skip" ,
227
+ obligation. repr( infcx. tcx) ) ;
228
+ return ;
229
+ }
230
+ }
231
+
173
232
debug ! ( "register_predicate({})" , obligation. repr( infcx. tcx) ) ;
174
233
self . predicates . push ( obligation) ;
175
234
}
@@ -178,6 +237,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
178
237
body_id : ast:: NodeId )
179
238
-> & [ RegionObligation < ' tcx > ]
180
239
{
240
+ assert ! ( self . stored_region_obligations. is_none( ) ,
241
+ "can't get region obligations within a transaction" ) ;
181
242
match self . region_obligations . get ( & body_id) {
182
243
None => Default :: default ( ) ,
183
244
Some ( vec) => vec,
0 commit comments