@@ -12,15 +12,13 @@ use std::fmt;
1212use std:: io;
1313use std:: io:: { Read , Write } ;
1414use std:: num:: NonZero ;
15- use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
1615
17- use smallvec:: { smallvec, SmallVec } ;
1816use tracing:: { debug, trace} ;
1917
2018use rustc_ast:: LitKind ;
2119use rustc_attr:: InlineAttr ;
2220use rustc_data_structures:: fx:: FxHashMap ;
23- use rustc_data_structures:: sync:: { HashMapExt , Lock } ;
21+ use rustc_data_structures:: sync:: Lock ;
2422use rustc_errors:: ErrorGuaranteed ;
2523use rustc_hir:: def_id:: { DefId , LocalDefId } ;
2624use rustc_macros:: { HashStable , TyDecodable , TyEncodable , TypeFoldable , TypeVisitable } ;
@@ -159,14 +157,9 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
159157 }
160158}
161159
162- // Used to avoid infinite recursion when decoding cyclic allocations.
163- type DecodingSessionId = NonZero < u32 > ;
164-
165160#[ derive( Clone ) ]
166161enum State {
167162 Empty ,
168- InProgressNonAlloc ( SmallVec < [ DecodingSessionId ; 1 ] > ) ,
169- InProgress ( SmallVec < [ DecodingSessionId ; 1 ] > , AllocId ) ,
170163 Done ( AllocId ) ,
171164}
172165
@@ -180,13 +173,7 @@ pub struct AllocDecodingState {
180173impl AllocDecodingState {
181174 #[ inline]
182175 pub fn new_decoding_session ( & self ) -> AllocDecodingSession < ' _ > {
183- static DECODER_SESSION_ID : AtomicU32 = AtomicU32 :: new ( 0 ) ;
184- let counter = DECODER_SESSION_ID . fetch_add ( 1 , Ordering :: SeqCst ) ;
185-
186- // Make sure this is never zero.
187- let session_id = DecodingSessionId :: new ( ( counter & 0x7FFFFFFF ) + 1 ) . unwrap ( ) ;
188-
189- AllocDecodingSession { state : self , session_id }
176+ AllocDecodingSession { state : self }
190177 }
191178
192179 pub fn new ( data_offsets : Vec < u64 > ) -> Self {
@@ -200,7 +187,6 @@ impl AllocDecodingState {
200187#[ derive( Copy , Clone ) ]
201188pub struct AllocDecodingSession < ' s > {
202189 state : & ' s AllocDecodingState ,
203- session_id : DecodingSessionId ,
204190}
205191
206192impl < ' s > AllocDecodingSession < ' s > {
@@ -220,106 +206,62 @@ impl<'s> AllocDecodingSession<'s> {
220206 ( alloc_kind, decoder. position ( ) )
221207 } ) ;
222208
209+ // We are going to hold this lock during the entire decoding of this allocation, which may
210+ // require that we decode other allocations. This cannot deadlock for two reasons:
211+ //
212+ // At the time of writing, it is only possible to create an allocation that contains a pointer
213+ // to itself using the const_allocate intrinsic (which is for testing only), and even attempting
214+ // to evaluate such consts blows the stack. If we ever grow a mechanism for producing
215+ // cyclic allocations, we will need a new strategy for decoding that doesn't bring back
216+ // https://github.com/rust-lang/rust/issues/126741.
217+ //
218+ // It is also impossible to create two allocations (call them A and B) where A is a pointer to B, and B
219+ // is a pointer to A, because attempting to evaluate either of those consts will produce a
220+ // query cycle, failing compilation.
221+ let mut entry = self . state . decoding_state [ idx] . lock ( ) ;
223222 // Check the decoding state to see if it's already decoded or if we should
224223 // decode it here.
225- let alloc_id = {
226- let mut entry = self . state . decoding_state [ idx] . lock ( ) ;
227-
228- match * entry {
229- State :: Done ( alloc_id) => {
230- return alloc_id;
231- }
232- ref mut entry @ State :: Empty => {
233- // We are allowed to decode.
234- match alloc_kind {
235- AllocDiscriminant :: Alloc => {
236- // If this is an allocation, we need to reserve an
237- // `AllocId` so we can decode cyclic graphs.
238- let alloc_id = decoder. interner ( ) . reserve_alloc_id ( ) ;
239- * entry = State :: InProgress ( smallvec ! [ self . session_id] , alloc_id) ;
240- Some ( alloc_id)
241- }
242- AllocDiscriminant :: Fn
243- | AllocDiscriminant :: Static
244- | AllocDiscriminant :: VTable => {
245- // Fns and statics cannot be cyclic, and their `AllocId`
246- // is determined later by interning.
247- * entry = State :: InProgressNonAlloc ( smallvec ! [ self . session_id] ) ;
248- None
249- }
250- }
251- }
252- State :: InProgressNonAlloc ( ref mut sessions) => {
253- if sessions. contains ( & self . session_id ) {
254- bug ! ( "this should be unreachable" ) ;
255- } else {
256- // Start decoding concurrently.
257- sessions. push ( self . session_id ) ;
258- None
259- }
260- }
261- State :: InProgress ( ref mut sessions, alloc_id) => {
262- if sessions. contains ( & self . session_id ) {
263- // Don't recurse.
264- return alloc_id;
265- } else {
266- // Start decoding concurrently.
267- sessions. push ( self . session_id ) ;
268- Some ( alloc_id)
269- }
270- }
271- }
272- } ;
224+ if let State :: Done ( alloc_id) = * entry {
225+ return alloc_id;
226+ }
273227
274228 // Now decode the actual data.
275229 let alloc_id = decoder. with_position ( pos, |decoder| {
276230 match alloc_kind {
277231 AllocDiscriminant :: Alloc => {
232+ trace ! ( "creating memory alloc ID" ) ;
278233 let alloc = <ConstAllocation < ' tcx > as Decodable < _ > >:: decode ( decoder) ;
279- // We already have a reserved `AllocId`.
280- let alloc_id = alloc_id. unwrap ( ) ;
281- trace ! ( "decoded alloc {:?}: {:#?}" , alloc_id, alloc) ;
282- decoder. interner ( ) . set_alloc_id_same_memory ( alloc_id, alloc) ;
283- alloc_id
234+ trace ! ( "decoded alloc {:?}" , alloc) ;
235+ decoder. interner ( ) . reserve_and_set_memory_alloc ( alloc)
284236 }
285237 AllocDiscriminant :: Fn => {
286- assert ! ( alloc_id. is_none( ) ) ;
287238 trace ! ( "creating fn alloc ID" ) ;
288239 let instance = ty:: Instance :: decode ( decoder) ;
289240 trace ! ( "decoded fn alloc instance: {:?}" , instance) ;
290241 let unique = bool:: decode ( decoder) ;
291242 // Here we cannot call `reserve_and_set_fn_alloc` as that would use a query, which
292243 // is not possible in this context. That's why the allocation stores
293244 // whether it is unique or not.
294- let alloc_id =
295- decoder. interner ( ) . reserve_and_set_fn_alloc_internal ( instance, unique) ;
296- alloc_id
245+ decoder. interner ( ) . reserve_and_set_fn_alloc_internal ( instance, unique)
297246 }
298247 AllocDiscriminant :: VTable => {
299- assert ! ( alloc_id. is_none( ) ) ;
300248 trace ! ( "creating vtable alloc ID" ) ;
301249 let ty = <Ty < ' _ > as Decodable < D > >:: decode ( decoder) ;
302250 let poly_trait_ref =
303251 <Option < ty:: PolyExistentialTraitRef < ' _ > > as Decodable < D > >:: decode ( decoder) ;
304252 trace ! ( "decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}" ) ;
305- let alloc_id =
306- decoder. interner ( ) . reserve_and_set_vtable_alloc ( ty, poly_trait_ref) ;
307- alloc_id
253+ decoder. interner ( ) . reserve_and_set_vtable_alloc ( ty, poly_trait_ref)
308254 }
309255 AllocDiscriminant :: Static => {
310- assert ! ( alloc_id. is_none( ) ) ;
311256 trace ! ( "creating extern static alloc ID" ) ;
312257 let did = <DefId as Decodable < D > >:: decode ( decoder) ;
313258 trace ! ( "decoded static def-ID: {:?}" , did) ;
314- let alloc_id = decoder. interner ( ) . reserve_and_set_static_alloc ( did) ;
315- alloc_id
259+ decoder. interner ( ) . reserve_and_set_static_alloc ( did)
316260 }
317261 }
318262 } ) ;
319263
320- self . state . decoding_state [ idx] . with_lock ( |entry| {
321- * entry = State :: Done ( alloc_id) ;
322- } ) ;
264+ * entry = State :: Done ( alloc_id) ;
323265
324266 alloc_id
325267 }
@@ -563,12 +505,6 @@ impl<'tcx> TyCtxt<'tcx> {
563505 bug ! ( "tried to set allocation ID {id:?}, but it was already existing as {old:#?}" ) ;
564506 }
565507 }
566-
567- /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
568- /// twice for the same `(AllocId, Allocation)` pair.
569- fn set_alloc_id_same_memory ( self , id : AllocId , mem : ConstAllocation < ' tcx > ) {
570- self . alloc_map . lock ( ) . alloc_map . insert_same ( id, GlobalAlloc :: Memory ( mem) ) ;
571- }
572508}
573509
574510////////////////////////////////////////////////////////////////////////////////
0 commit comments