Skip to content

Commit c188565

Browse files
committed
Auto merge of rust-lang#159623 - camelid:good-impls, r=notriddle,GuillaumeGomez
rustdoc: Only build extern trait impls if needed or, finally remove the `BadImplStripper`! Building inlined impls is expensive, and most of them end up being unneeded and stripped later in this function. So we should filter them ahead of time. This requires inlining external auto traits when we construct auto impls, since this no longer happens as a side effect. We inline external impls when they are * for generics (i.e., blanket impls) * for primitive types (probably this should be handled below really) * for a type (inlined) in the current crate * of a trait (inlined) in the current crate * of `Deref` These rules are based on the existing filtering rules that are applied after building the inlined impls. After doing the filtering ahead of time, we can remove the downstream filtering. Thus, we can finally remove `BadImplStripper` and unused deref-following logic. It appears that all this `Deref` logic was pointless in the first place since following derefs already happens elsewhere in rustdoc, where it is actually needed.
2 parents b803f36 + afaa983 commit c188565

5 files changed

Lines changed: 26 additions & 111 deletions

File tree

src/librustdoc/clean/auto_trait.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ fn synthesize_auto_trait_impl<'tcx>(
114114
auto_trait::AutoTraitResult::ExplicitImpl => return None,
115115
};
116116

117+
super::inline::record_extern_trait(cx, trait_def_id);
118+
117119
Some(clean::Item {
118120
inner: Box::new(clean::ItemInner {
119121
name: None,

src/librustdoc/clean/inline.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::ty::{self, TyCtxt};
1414
use rustc_span::def_id::LOCAL_CRATE;
1515
use rustc_span::hygiene::MacroKind;
1616
use rustc_span::symbol::{Symbol, sym};
17-
use tracing::{debug, trace};
17+
use tracing::{debug, instrument, trace};
1818

1919
use super::{Item, extract_cfg_from_attrs};
2020
use crate::clean::{
@@ -453,6 +453,7 @@ pub(crate) fn merge_attrs(
453453
}
454454

455455
/// Inline an `impl`, inherent or of a trait. The `did` must be for an `impl`.
456+
#[instrument(level = "debug", skip(cx, ret))]
456457
pub(crate) fn build_impl(
457458
cx: &mut DocContext<'_>,
458459
did: DefId,

src/librustdoc/passes/collect_trait_impls.rs

Lines changed: 20 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
55
use rustc_data_structures::fx::FxHashSet;
66
use rustc_hir::attrs::{AttributeKind, DocAttribute};
7-
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
7+
use rustc_hir::def_id::LOCAL_CRATE;
88
use rustc_hir::{Attribute, find_attr};
99
use rustc_middle::ty;
10-
use tracing::debug;
1110

1211
use super::Pass;
1312
use crate::clean::*;
@@ -53,7 +52,25 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
5352
for &cnum in tcx.crates(()) {
5453
for &impl_def_id in tcx.trait_impls_in_crate(cnum) {
5554
cx.with_param_env(impl_def_id, |cx| {
56-
inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
55+
let opt_trait_ref = tcx.impl_opt_trait_ref(impl_def_id);
56+
let self_ty = tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip();
57+
let self_ty =
58+
clean_middle_ty(ty::Binder::dummy(self_ty), cx, Some(impl_def_id), None);
59+
if self_ty.is_full_generic()
60+
|| self_ty
61+
.primitive_type()
62+
.is_some_and(|primitive| prims.contains(&primitive))
63+
|| self_ty
64+
.def_id(&cx.cache)
65+
.is_some_and(|did| crate_items.contains(&ItemId::DefId(did)))
66+
|| opt_trait_ref.is_some_and(|trait_ref| {
67+
crate_items.contains(&ItemId::DefId(trait_ref.def_id()))
68+
|| Some(trait_ref.def_id()) == tcx.lang_items().deref_trait()
69+
|| tcx.is_doc_notable_trait(trait_ref.def_id())
70+
})
71+
{
72+
inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
73+
}
5774
});
5875
}
5976
}
@@ -126,86 +143,6 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
126143
}
127144
});
128145

129-
let mut cleaner = BadImplStripper { prims, items: crate_items, cache: &cx.cache };
130-
let mut type_did_to_deref_target: DefIdMap<&Type> = DefIdMap::default();
131-
132-
// Follow all `Deref` targets of included items and recursively add them as valid
133-
fn add_deref_target(
134-
cx: &DocContext<'_>,
135-
map: &DefIdMap<&Type>,
136-
cleaner: &mut BadImplStripper<'_>,
137-
targets: &mut DefIdSet,
138-
type_did: DefId,
139-
) {
140-
if let Some(target) = map.get(&type_did) {
141-
debug!("add_deref_target: type {:?}, target {:?}", type_did, target);
142-
if let Some(target_prim) = target.primitive_type() {
143-
cleaner.prims.insert(target_prim);
144-
} else if let Some(target_did) = target.def_id(&cx.cache) {
145-
// `impl Deref<Target = S> for S`
146-
if !targets.insert(target_did) {
147-
// Avoid infinite cycles
148-
return;
149-
}
150-
cleaner.items.insert(target_did.into());
151-
add_deref_target(cx, map, cleaner, targets, target_did);
152-
}
153-
}
154-
}
155-
156-
// scan through included items ahead of time to splice in Deref targets to the "valid" sets
157-
for it in new_items_external.iter().chain(new_items_local.iter()) {
158-
if let ImplItem(Impl { ref for_, ref trait_, ref items, polarity, .. }) = it.kind
159-
&& trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait()
160-
&& polarity != ty::ImplPolarity::Negative
161-
&& cleaner.keep_impl(for_, true)
162-
{
163-
let target = items
164-
.iter()
165-
.find_map(|item| match item.kind {
166-
AssocTypeItem(ref t, _) => Some(&t.type_),
167-
_ => None,
168-
})
169-
.expect("Deref impl without Target type");
170-
171-
if let Some(prim) = target.primitive_type() {
172-
cleaner.prims.insert(prim);
173-
} else if let Some(did) = target.def_id(&cx.cache) {
174-
cleaner.items.insert(did.into());
175-
}
176-
if let Some(for_did) = for_.def_id(&cx.cache)
177-
&& type_did_to_deref_target.insert(for_did, target).is_none()
178-
// Since only the `DefId` portion of the `Type` instances is known to be same for both the
179-
// `Deref` target type and the impl for type positions, this map of types is keyed by
180-
// `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
181-
&& cleaner.keep_impl_with_def_id(for_did.into())
182-
{
183-
let mut targets = DefIdSet::default();
184-
targets.insert(for_did);
185-
add_deref_target(
186-
cx,
187-
&type_did_to_deref_target,
188-
&mut cleaner,
189-
&mut targets,
190-
for_did,
191-
);
192-
}
193-
}
194-
}
195-
196-
// Filter out external items that are not needed
197-
new_items_external.retain(|it| {
198-
if let ImplItem(Impl { ref for_, ref trait_, ref kind, .. }) = it.kind {
199-
cleaner.keep_impl(
200-
for_,
201-
trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait(),
202-
) || trait_.as_ref().is_some_and(|t| cleaner.keep_impl_with_def_id(t.def_id().into()))
203-
|| kind.is_blanket()
204-
} else {
205-
true
206-
}
207-
});
208-
209146
if let ModuleItem(Module { items, .. }) = &mut krate.module.inner.kind {
210147
items.extend(synth_impls);
211148
items.extend(new_items_external);
@@ -264,28 +201,3 @@ impl DocVisitor<'_> for ItemAndAliasCollector<'_> {
264201
self.visit_item_recur(i)
265202
}
266203
}
267-
268-
struct BadImplStripper<'a> {
269-
prims: FxHashSet<PrimitiveType>,
270-
items: FxHashSet<ItemId>,
271-
cache: &'a Cache,
272-
}
273-
274-
impl BadImplStripper<'_> {
275-
fn keep_impl(&self, ty: &Type, is_deref: bool) -> bool {
276-
if let Generic(_) = ty {
277-
// keep impls made on generics
278-
true
279-
} else if let Some(prim) = ty.primitive_type() {
280-
self.prims.contains(&prim)
281-
} else if let Some(did) = ty.def_id(self.cache) {
282-
is_deref || self.keep_impl_with_def_id(did.into())
283-
} else {
284-
false
285-
}
286-
}
287-
288-
fn keep_impl_with_def_id(&self, item_id: ItemId) -> bool {
289-
self.items.contains(&item_id)
290-
}
291-
}

tests/rustdoc-html/notable-trait/spotlight-from-dependency.odd.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tests/rustdoc-html/where.SWhere_TraitWhere_item-decl.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Provided methods
66
fn <a href="#method.func" class="fn">func</a>(self)
77
<span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
8-
<span class="item-spacer" /> fn <a href="#method.lines" class="fn">lines</a>(self) -&gt; <a class="struct" href="{{channel}}/alloc/io/util/struct.Lines.html" title="struct alloc::io::util::Lines">Lines</a>&lt;Self&gt;
8+
<span class="item-spacer" /> fn <a href="#method.lines" class="fn">lines</a>(self) -&gt; <a class="struct" href="{{channel}}/alloc/io/util/struct.Lines.html" title="struct alloc::io::util::Lines">Lines</a>&lt;Self&gt; <a href="#" class="tooltip" data-notable-ty="Lines&lt;Self&gt;">&#9432;</a>
99
<span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
1010
<span class="item-spacer" /> fn <a href="#method.merge" class="fn">merge</a>&lt;T&gt;(self, a: T)
1111
<span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,

0 commit comments

Comments
 (0)