|
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,16 @@ 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 |
| 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 |
60 | 71 | .typeck_results()
|
61 | 72 | .type_dependent_def(expr.hir_id)
|
62 |
| - .map(|(kind, def_id)| Res::Def(kind, def_id)), |
| 73 | + .map(|(kind, def_id)| Res::Def(kind, def_id)) |
| 74 | + }, |
63 | 75 | hir::ExprKind::Closure(&hir::Closure { def_id, .. }) => {
|
64 | 76 | self.reachable_symbols.insert(def_id);
|
65 | 77 | None
|
@@ -394,6 +406,7 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
394 | 406 | || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
|
395 | 407 | }
|
396 | 408 |
|
| 409 | +/// See module-level doc comment above. |
397 | 410 | fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
|
398 | 411 | let effective_visibilities = &tcx.effective_visibilities(());
|
399 | 412 |
|
@@ -427,10 +440,10 @@ fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet {
|
427 | 440 | }
|
428 | 441 | }
|
429 | 442 | {
|
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. |
| 443 | + // As explained above, we have to mark all functions called from reachable |
| 444 | + // `item_might_be_inlined` items as reachable. The issue is, when those functions are |
| 445 | + // generic and call a trait method, we have no idea where that call goes! So, we |
| 446 | + // conservatively mark all trait impl items as reachable. |
434 | 447 | // FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
|
435 | 448 | // items of non-exported traits (or maybe all local traits?) unless their respective
|
436 | 449 | // trait items are used from inlinable code through method call syntax or UFCS, or their
|
|
0 commit comments