66// reachable as well.
77
88use hir:: def_id:: LocalDefIdSet ;
9+ use rustc_data_structures:: stack:: ensure_sufficient_stack;
910use rustc_hir as hir;
1011use rustc_hir:: def:: { DefKind , Res } ;
1112use rustc_hir:: def_id:: { DefId , LocalDefId } ;
@@ -15,7 +16,8 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}
1516use rustc_middle:: middle:: privacy:: { self , Level } ;
1617use rustc_middle:: mir:: interpret:: { ConstAllocation , GlobalAlloc } ;
1718use rustc_middle:: query:: Providers ;
18- use rustc_middle:: ty:: { self , TyCtxt } ;
19+ use rustc_middle:: ty:: { self , ExistentialTraitRef , TyCtxt } ;
20+ use rustc_privacy:: DefIdVisitor ;
1921use rustc_session:: config:: CrateType ;
2022use rustc_target:: spec:: abi:: Abi ;
2123
@@ -65,23 +67,8 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
6567 _ => None ,
6668 } ;
6769
68- if let Some ( res) = res
69- && let Some ( def_id) = res. opt_def_id ( ) . and_then ( |el| el. as_local ( ) )
70- {
71- if self . def_id_represents_local_inlined_item ( def_id. to_def_id ( ) ) {
72- self . worklist . push ( def_id) ;
73- } else {
74- match res {
75- // Reachable constants and reachable statics can have their contents inlined
76- // into other crates. Mark them as reachable and recurse into their body.
77- Res :: Def ( DefKind :: Const | DefKind :: AssocConst | DefKind :: Static { .. } , _) => {
78- self . worklist . push ( def_id) ;
79- }
80- _ => {
81- self . reachable_symbols . insert ( def_id) ;
82- }
83- }
84- }
70+ if let Some ( res) = res {
71+ self . propagate_item ( res) ;
8572 }
8673
8774 intravisit:: walk_expr ( self , expr)
@@ -198,17 +185,9 @@ impl<'tcx> ReachableContext<'tcx> {
198185 hir:: ItemKind :: Const ( _, _, init) => {
199186 self . visit_nested_body ( init) ;
200187 }
201-
202- // Reachable statics are inlined if read from another constant or static
203- // in other crates. Additionally anonymous nested statics may be created
204- // when evaluating a static, so preserve those, too.
205- hir:: ItemKind :: Static ( _, _, init) => {
206- // FIXME(oli-obk): remove this body walking and instead walk the evaluated initializer
207- // to find nested items that end up in the final value instead of also marking symbols
208- // as reachable that are only needed for evaluation.
209- self . visit_nested_body ( init) ;
188+ hir:: ItemKind :: Static ( ..) => {
210189 if let Ok ( alloc) = self . tcx . eval_static_initializer ( item. owner_id . def_id ) {
211- self . propagate_statics_from_alloc ( item . owner_id . def_id , alloc) ;
190+ self . propagate_from_alloc ( alloc) ;
212191 }
213192 }
214193
@@ -279,28 +258,89 @@ impl<'tcx> ReachableContext<'tcx> {
279258 }
280259 }
281260
282- /// Finds anonymous nested statics created for nested allocations and adds them to `reachable_symbols`.
283- fn propagate_statics_from_alloc ( & mut self , root : LocalDefId , alloc : ConstAllocation < ' tcx > ) {
261+ /// Finds things to add to `reachable_symbols` within allocations.
262+ /// In contrast to visit_nested_body this ignores things that were only needed to evaluate
263+ /// the allocation.
264+ fn propagate_from_alloc ( & mut self , alloc : ConstAllocation < ' tcx > ) {
284265 if !self . any_library {
285266 return ;
286267 }
287268 for ( _, prov) in alloc. 0 . provenance ( ) . ptrs ( ) . iter ( ) {
288269 match self . tcx . global_alloc ( prov. alloc_id ( ) ) {
289270 GlobalAlloc :: Static ( def_id) => {
290- if let Some ( def_id) = def_id. as_local ( )
291- && self . tcx . local_parent ( def_id) == root
292- // This is the main purpose of this function: add the def_id we find
293- // to `reachable_symbols`.
294- && self . reachable_symbols . insert ( def_id)
295- && let Ok ( alloc) = self . tcx . eval_static_initializer ( def_id)
296- {
297- self . propagate_statics_from_alloc ( root, alloc) ;
271+ self . propagate_item ( Res :: Def ( self . tcx . def_kind ( def_id) , def_id) )
272+ }
273+ GlobalAlloc :: Function ( instance) => {
274+ // Manually visit to actually see the instance's `DefId`. Type visitors won't see it
275+ self . propagate_item ( Res :: Def (
276+ self . tcx . def_kind ( instance. def_id ( ) ) ,
277+ instance. def_id ( ) ,
278+ ) ) ;
279+ self . visit ( instance. args ) ;
280+ }
281+ GlobalAlloc :: VTable ( ty, trait_ref) => {
282+ self . visit ( ty) ;
283+ // Manually visit to actually see the trait's `DefId`. Type visitors won't see it
284+ if let Some ( trait_ref) = trait_ref {
285+ let ExistentialTraitRef { def_id, args } = trait_ref. skip_binder ( ) ;
286+ self . visit_def_id ( def_id, "" , & "" ) ;
287+ self . visit ( args) ;
298288 }
299289 }
300- GlobalAlloc :: Function ( _ ) | GlobalAlloc :: VTable ( _ , _ ) | GlobalAlloc :: Memory ( _ ) => { }
290+ GlobalAlloc :: Memory ( alloc ) => self . propagate_from_alloc ( alloc ) ,
301291 }
302292 }
303293 }
294+
295+ fn propagate_item ( & mut self , res : Res ) {
296+ let Res :: Def ( kind, def_id) = res else { return } ;
297+ let Some ( def_id) = def_id. as_local ( ) else { return } ;
298+ match kind {
299+ DefKind :: Static { nested : true , .. } => {
300+ // This is the main purpose of this function: add the def_id we find
301+ // to `reachable_symbols`.
302+ if self . reachable_symbols . insert ( def_id) {
303+ if let Ok ( alloc) = self . tcx . eval_static_initializer ( def_id) {
304+ // This cannot cause infinite recursion, because we abort by inserting into the
305+ // work list once we hit a normal static. Nested statics, even if they somehow
306+ // become recursive, are also not infinitely recursing, because of the
307+ // `reachable_symbols` check above.
308+ // We still need to protect against stack overflow due to deeply nested statics.
309+ ensure_sufficient_stack ( || self . propagate_from_alloc ( alloc) ) ;
310+ }
311+ }
312+ }
313+ // Reachable constants and reachable statics can have their contents inlined
314+ // into other crates. Mark them as reachable and recurse into their body.
315+ DefKind :: Const | DefKind :: AssocConst | DefKind :: Static { .. } => {
316+ self . worklist . push ( def_id) ;
317+ }
318+ _ => {
319+ if self . def_id_represents_local_inlined_item ( def_id. to_def_id ( ) ) {
320+ self . worklist . push ( def_id) ;
321+ } else {
322+ self . reachable_symbols . insert ( def_id) ;
323+ }
324+ }
325+ }
326+ }
327+ }
328+
329+ impl < ' tcx > DefIdVisitor < ' tcx > for ReachableContext < ' tcx > {
330+ type Result = ( ) ;
331+
332+ fn tcx ( & self ) -> TyCtxt < ' tcx > {
333+ self . tcx
334+ }
335+
336+ fn visit_def_id (
337+ & mut self ,
338+ def_id : DefId ,
339+ _kind : & str ,
340+ _descr : & dyn std:: fmt:: Display ,
341+ ) -> Self :: Result {
342+ self . propagate_item ( Res :: Def ( self . tcx . def_kind ( def_id) , def_id) )
343+ }
304344}
305345
306346fn check_item < ' tcx > (
0 commit comments