Skip to content

Commit b7a9816

Browse files
committed
extend doc comment for reachability set computation
1 parent b7dcabe commit b7a9816

File tree

1 file changed

+37
-23
lines changed

1 file changed

+37
-23
lines changed

compiler/rustc_passes/src/reachable.rs

+37-23
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
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
811
use hir::def_id::LocalDefIdSet;
912
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -21,7 +24,9 @@ use rustc_privacy::DefIdVisitor;
2124
use rustc_session::config::CrateType;
2225
use 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.
397411
fn 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

Comments
 (0)