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.
7
10
8
11
use hir:: def_id:: LocalDefIdSet ;
9
12
use rustc_data_structures:: stack:: ensure_sufficient_stack;
@@ -21,7 +24,9 @@ use rustc_privacy::DefIdVisitor;
21
24
use rustc_session:: config:: CrateType ;
22
25
use rustc_target:: spec:: abi:: Abi ;
23
26
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 {
25
30
tcx. generics_of ( def_id) . requires_monomorphization ( tcx)
26
31
|| tcx. cross_crate_inlinable ( def_id)
27
32
|| tcx. is_const_fn ( def_id)
@@ -54,12 +59,20 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
54
59
fn visit_expr ( & mut self , expr : & ' tcx hir:: Expr < ' tcx > ) {
55
60
let res = match expr. kind {
56
61
hir:: ExprKind :: Path ( ref qpath) => {
62
+ // This covers fn ptr casts but also "non-method" calls.
57
63
Some ( self . typeck_results ( ) . qpath_res ( qpath, expr. hir_id ) )
58
64
}
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
+ }
63
76
hir:: ExprKind :: Closure ( & hir:: Closure { def_id, .. } ) => {
64
77
self . reachable_symbols . insert ( def_id) ;
65
78
None
@@ -96,16 +109,16 @@ impl<'tcx> ReachableContext<'tcx> {
96
109
. expect ( "`ReachableContext::typeck_results` called outside of body" )
97
110
}
98
111
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 {
102
115
let Some ( def_id) = def_id. as_local ( ) else {
103
116
return false ;
104
117
} ;
105
118
106
119
match self . tcx . hir_node_by_def_id ( def_id) {
107
120
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 ( ) ) ,
109
122
_ => false ,
110
123
} ,
111
124
Node :: TraitItem ( trait_method) => match trait_method. kind {
@@ -117,7 +130,7 @@ impl<'tcx> ReachableContext<'tcx> {
117
130
Node :: ImplItem ( impl_item) => match impl_item. kind {
118
131
hir:: ImplItemKind :: Const ( ..) => true ,
119
132
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 ( ) )
121
134
}
122
135
hir:: ImplItemKind :: Type ( _) => false ,
123
136
} ,
@@ -174,7 +187,7 @@ impl<'tcx> ReachableContext<'tcx> {
174
187
Node :: Item ( item) => {
175
188
match item. kind {
176
189
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 ( ) ) {
178
191
self . visit_nested_body ( body) ;
179
192
}
180
193
}
@@ -228,7 +241,7 @@ impl<'tcx> ReachableContext<'tcx> {
228
241
self . visit_nested_body ( body) ;
229
242
}
230
243
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 ( ) ) {
232
245
self . visit_nested_body ( body)
233
246
}
234
247
}
@@ -316,7 +329,7 @@ impl<'tcx> ReachableContext<'tcx> {
316
329
self . worklist . push ( def_id) ;
317
330
}
318
331
_ => {
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 ( ) ) {
320
333
self . worklist . push ( def_id) ;
321
334
} else {
322
335
self . reachable_symbols . insert ( def_id) ;
@@ -394,6 +407,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
394
407
|| codegen_attrs. flags . contains ( CodegenFnAttrFlags :: USED_LINKER )
395
408
}
396
409
410
+ /// See module-level doc comment above.
397
411
fn reachable_set ( tcx : TyCtxt < ' _ > , ( ) : ( ) ) -> LocalDefIdSet {
398
412
let effective_visibilities = & tcx. effective_visibilities ( ( ) ) ;
399
413
@@ -427,10 +441,10 @@ fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
427
441
}
428
442
}
429
443
{
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.
434
448
// FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
435
449
// items of non-exported traits (or maybe all local traits?) unless their respective
436
450
// trait items are used from inlinable code through method call syntax or UFCS, or their
0 commit comments