|
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, to determine which items |
| 2 | +//! need to have their metadata (and possibly their AST) serialized. |
| 3 | +//! |
| 4 | +//! This set is *not* transitively closed, i.e., in general the set only contains definitions that |
| 5 | +//! can be reached *directly* via an exported name, not private functions that can only be reached |
| 6 | +//! transitively. |
| 7 | +//! |
| 8 | +//! However, there's a catch: if an item is generic or cross-crate inlinable, then it will have its |
| 9 | +//! code generated by some downstream crate. Now if that item calls private monomorphic |
| 10 | +//! non-cross-crate-inlinable items, then those can be reached by the code generated by the |
| 11 | +//! downstream create! Therefore, when a reachable thing is cross-crate inlinable or generic, it |
| 12 | +//! makes all other functions that it references reachable as well. |
7 | 13 |
|
8 | 14 | use hir::def_id::LocalDefIdSet;
|
9 | 15 | use rustc_data_structures::stack::ensure_sufficient_stack;
|
@@ -56,10 +62,15 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> {
|
56 | 62 | hir::ExprKind::Path(ref qpath) => {
|
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 | + // If this is a method call on a generic type, we might not be able to find the |
| 67 | + // callee. That's why `reachable_set` also adds all potential callees for such |
| 68 | + // calls, i.e. all trait impl items, to the reachable set. So here we only worry |
| 69 | + // about the calls we can identify. |
| 70 | + self.typeck_results() |
| 71 | + .type_dependent_def(expr.hir_id) |
| 72 | + .map(|(kind, def_id)| Res::Def(kind, def_id)) |
| 73 | + } |
63 | 74 | hir::ExprKind::Closure(&hir::Closure { def_id, .. }) => {
|
64 | 75 | self.reachable_symbols.insert(def_id);
|
65 | 76 | None
|
@@ -394,6 +405,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
394 | 405 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
|
395 | 406 | }
|
396 | 407 |
|
| 408 | +/// See module-level doc comment above. |
397 | 409 | fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
|
398 | 410 | let effective_visibilities = &tcx.effective_visibilities(());
|
399 | 411 |
|
@@ -427,10 +439,10 @@ fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
|
427 | 439 | }
|
428 | 440 | }
|
429 | 441 | {
|
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. |
| 442 | + // As explained above, we have to mark all functions called from reachable |
| 443 | + // `item_might_be_inlined` items as reachable. The issue is, when those functions are |
| 444 | + // generic and call a trait method, we have no idea where that call goes! So, we |
| 445 | + // conservatively mark all trait impl items as reachable. |
434 | 446 | // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
|
435 | 447 | // items of non-exported traits (or maybe all local traits?) unless their respective
|
436 | 448 | // trait items are used from inlinable code through method call syntax or UFCS, or their
|
|
0 commit comments