1- // Finds items that are externally reachable, to determine which items
2- // need to have their metadata (and possibly their AST) serialized.
3- // All items that can be referred to through an exported name are
4- // reachable, and when a reachable thing is inline or generic, it
5- // makes all other generics or inline functions that it references
6- // reachable as well.
1+ //! Finds local items that are externally reachable, which means that other crates need access to
2+ //! their compiled machine code or their MIR.
3+ //!
4+ //! An item is "externally reachable" if it is relevant for other crates. This obviously includes
5+ //! all public items. However, some of these items cannot be compiled to machine code (because they
6+ //! are generic), and for some the machine code is not sufficient (because we want to cross-crate
7+ //! inline them, or run them in the compile-time MIR interpreter). These items "need cross-crate
8+ //! MIR". When a reachable function `f` needs cross-crate MIR, then all the functions it calls also
9+ //! become reachable, as they will be necessary to use the MIR of `f` from another crate.
710
811use hir:: def_id:: LocalDefIdSet ;
912use rustc_data_structures:: stack:: ensure_sufficient_stack;
@@ -21,7 +24,9 @@ use rustc_privacy::DefIdVisitor;
2124use rustc_session:: config:: CrateType ;
2225use rustc_target:: spec:: abi:: Abi ;
2326
24- fn item_might_be_inlined ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> bool {
27+ /// Determines whether this (assumed to be reachable) item "needs cross-crate MIR", i.e. whether
28+ /// another crate needs to be able to access this items' MIR.
29+ fn needs_cross_crate_mir ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> bool {
2530 tcx. generics_of ( def_id) . requires_monomorphization ( tcx)
2631 || tcx. cross_crate_inlinable ( def_id)
2732 || tcx. is_const_fn ( def_id)
@@ -54,12 +59,20 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
5459 fn visit_expr ( & mut self , expr : & ' tcx hir:: Expr < ' tcx > ) {
5560 let res = match expr. kind {
5661 hir:: ExprKind :: Path ( ref qpath) => {
62+ // This covers fn ptr casts but also "non-method" calls.
5763 Some ( self . typeck_results ( ) . qpath_res ( qpath, expr. hir_id ) )
5864 }
59- hir:: ExprKind :: MethodCall ( ..) => self
60- . typeck_results ( )
61- . type_dependent_def ( expr. hir_id )
62- . map ( |( kind, def_id) | Res :: Def ( kind, def_id) ) ,
65+ hir:: ExprKind :: MethodCall ( ..) => {
66+ // Method calls don't involve a full "path", so we need to determine the callee
67+ // based on the receiver type.
68+ // If this is a method call on a generic type, we might not be able to find the
69+ // callee. That's why `reachable_set` also adds all potential callees for such
70+ // calls, i.e. all trait impl items, to the reachable set. So here we only worry
71+ // about the calls we can identify.
72+ self . typeck_results ( )
73+ . type_dependent_def ( expr. hir_id )
74+ . map ( |( kind, def_id) | Res :: Def ( kind, def_id) )
75+ }
6376 hir:: ExprKind :: Closure ( & hir:: Closure { def_id, .. } ) => {
6477 self . reachable_symbols . insert ( def_id) ;
6578 None
@@ -96,16 +109,16 @@ impl<'tcx> ReachableContext<'tcx> {
96109 . expect ( "`ReachableContext::typeck_results` called outside of body" )
97110 }
98111
99- // Returns true if the given def ID represents a local item that is
100- // eligible for inlining and false otherwise .
101- fn def_id_represents_local_inlined_item ( & self , def_id : DefId ) -> bool {
112+ /// Returns true if the given def ID represents a local item where another crate may need access
113+ /// to its MIR .
114+ fn is_local_cross_crate_mir ( & self , def_id : DefId ) -> bool {
102115 let Some ( def_id) = def_id. as_local ( ) else {
103116 return false ;
104117 } ;
105118
106119 match self . tcx . hir_node_by_def_id ( def_id) {
107120 Node :: Item ( item) => match item. kind {
108- hir:: ItemKind :: Fn ( ..) => item_might_be_inlined ( self . tcx , def_id. into ( ) ) ,
121+ hir:: ItemKind :: Fn ( ..) => needs_cross_crate_mir ( self . tcx , def_id. into ( ) ) ,
109122 _ => false ,
110123 } ,
111124 Node :: TraitItem ( trait_method) => match trait_method. kind {
@@ -117,7 +130,7 @@ impl<'tcx> ReachableContext<'tcx> {
117130 Node :: ImplItem ( impl_item) => match impl_item. kind {
118131 hir:: ImplItemKind :: Const ( ..) => true ,
119132 hir:: ImplItemKind :: Fn ( ..) => {
120- item_might_be_inlined ( self . tcx , impl_item. hir_id ( ) . owner . to_def_id ( ) )
133+ needs_cross_crate_mir ( self . tcx , impl_item. hir_id ( ) . owner . to_def_id ( ) )
121134 }
122135 hir:: ImplItemKind :: Type ( _) => false ,
123136 } ,
@@ -174,7 +187,7 @@ impl<'tcx> ReachableContext<'tcx> {
174187 Node :: Item ( item) => {
175188 match item. kind {
176189 hir:: ItemKind :: Fn ( .., body) => {
177- if item_might_be_inlined ( self . tcx , item. owner_id . into ( ) ) {
190+ if needs_cross_crate_mir ( self . tcx , item. owner_id . into ( ) ) {
178191 self . visit_nested_body ( body) ;
179192 }
180193 }
@@ -228,7 +241,7 @@ impl<'tcx> ReachableContext<'tcx> {
228241 self . visit_nested_body ( body) ;
229242 }
230243 hir:: ImplItemKind :: Fn ( _, body) => {
231- if item_might_be_inlined ( self . tcx , impl_item. hir_id ( ) . owner . to_def_id ( ) ) {
244+ if needs_cross_crate_mir ( self . tcx , impl_item. hir_id ( ) . owner . to_def_id ( ) ) {
232245 self . visit_nested_body ( body)
233246 }
234247 }
@@ -316,7 +329,7 @@ impl<'tcx> ReachableContext<'tcx> {
316329 self . worklist . push ( def_id) ;
317330 }
318331 _ => {
319- if self . def_id_represents_local_inlined_item ( def_id. to_def_id ( ) ) {
332+ if self . is_local_cross_crate_mir ( def_id. to_def_id ( ) ) {
320333 self . worklist . push ( def_id) ;
321334 } else {
322335 self . reachable_symbols . insert ( def_id) ;
@@ -394,6 +407,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
394407 || codegen_attrs. flags . contains ( CodegenFnAttrFlags :: USED_LINKER )
395408}
396409
410+ /// See module-level doc comment above.
397411fn reachable_set ( tcx : TyCtxt < ' _ > , ( ) : ( ) ) -> LocalDefIdSet {
398412 let effective_visibilities = & tcx. effective_visibilities ( ( ) ) ;
399413
@@ -427,10 +441,10 @@ fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
427441 }
428442 }
429443 {
430- // Some methods from non-exported (completely private) trait impls still have to be
431- // reachable if they are called from inlinable code. Generally, it's not known until
432- // monomorphization if a specific trait impl item can be reachable or not. So, we
433- // conservatively mark all of them as reachable.
444+ // As explained above, we have to mark all functions called from reachable
445+ // `item_might_be_inlined` items as reachable. The issue is, when those functions are
446+ // generic and call a trait method, we have no idea where that call goes! So, we
447+ // conservatively mark all trait impl items as reachable.
434448 // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
435449 // items of non-exported traits (or maybe all local traits?) unless their respective
436450 // trait items are used from inlinable code through method call syntax or UFCS, or their
0 commit comments