diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 554b81b14cd3d..dffdcbb7ef0cf 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -71,7 +71,7 @@ fn synthesize_auto_trait_impl<'tcx>( ) -> Option { let tcx = cx.tcx; let trait_ref = ty::Binder::dummy(ty::TraitRef::new(tcx, trait_def_id, [ty])); - if !cx.generated_synthetics.insert((ty, trait_def_id)) { + if !cx.synthetic_auto_trait_impls.insert((ty, trait_def_id)) { debug!("already generated, aborting"); return None; } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 8dda831cac85b..6cf7c553c76bb 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -25,7 +25,7 @@ pub(crate) fn synthesize_blanket_impls( let mut blanket_impls = Vec::new(); for trait_def_id in tcx.visible_traits() { if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id) - || cx.generated_synthetics.contains(&(ty.skip_binder(), trait_def_id)) + || cx.synthetic_blanket_impls.contains(&(ty.skip_binder(), trait_def_id)) { continue; } @@ -81,7 +81,7 @@ pub(crate) fn synthesize_blanket_impls( } debug!("found applicable impl for trait ref {trait_ref:?}"); - cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); + cx.synthetic_blanket_impls.insert((ty.skip_binder(), trait_def_id)); blanket_impls.push(clean::Item { inner: Box::new(clean::ItemInner { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 21ce508d8560c..cb6465597218e 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -56,9 +56,18 @@ pub(crate) struct DocContext<'tcx> { pub(crate) current_type_aliases: DefIdMap, /// Table synthetic type parameter for `impl Trait` in argument position -> bounds pub(crate) impl_trait_bounds: FxHashMap>, - /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. - // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. - pub(crate) generated_synthetics: FxHashSet<(Ty<'tcx>, DefId)>, + + // FIXME: I'm pretty that the only reason we "need" these caches is because we also invoke + // `synthesize_auto_trait_and_blanket_impls` on all impls(!) for primitive types + // instead of calling it only once per primitive type (see also #97129). + // Get rid of that jank and remove both caches! + // + /// The set of auto-trait impls generated so far; identified by `(self_ty, trait_def_id)`. + pub(crate) synthetic_auto_trait_impls: FxHashSet<(Ty<'tcx>, DefId)>, + /// The set of blanket impls generated so far; identified by `(self_ty, trait_def_id)`. + pub(crate) synthetic_blanket_impls: FxHashSet<(Ty<'tcx>, DefId)>, + + /// All auto traits in the (visible) crate graph. pub(crate) auto_traits: Vec, /// This same cache is used throughout rustdoc, including in [`crate::html::render`]. pub(crate) cache: Cache, @@ -369,7 +378,8 @@ pub(crate) fn run_global_ctxt( args: Default::default(), current_type_aliases: Default::default(), impl_trait_bounds: Default::default(), - generated_synthetics: Default::default(), + synthetic_auto_trait_impls: Default::default(), + synthetic_blanket_impls: Default::default(), auto_traits, cache: Cache::new(render_options.document_private, render_options.document_hidden), inlined: FxHashSet::default(), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8108316a856ba..adb53b060b69c 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1397,13 +1397,13 @@ fn render_all_impls( mut w: impl Write, cx: &Context<'_>, containing_item: &clean::Item, - concrete: &[&Impl], - synthetic: &[&Impl], - blanket_impl: &[&Impl], + concrete_impls: &[&Impl], + auto_trait_impls: &[&Impl], + blanket_impls: &[&Impl], ) -> fmt::Result { let impls = { let mut buf = String::new(); - render_impls(cx, &mut buf, concrete, containing_item, true)?; + render_impls(cx, &mut buf, concrete_impls, containing_item, true)?; buf }; if !impls.is_empty() { @@ -1414,23 +1414,24 @@ fn render_all_impls( )?; } - if !synthetic.is_empty() { + if !auto_trait_impls.is_empty() { + // FIXME: Change the ID to `auto-trait-implementations-list`! write!( w, "{}
", write_impl_section_heading("Auto Trait Implementations", "synthetic-implementations",) )?; - render_impls(cx, &mut w, synthetic, containing_item, false)?; + render_impls(cx, &mut w, auto_trait_impls, containing_item, false)?; w.write_str("
")?; } - if !blanket_impl.is_empty() { + if !blanket_impls.is_empty() { write!( w, "{}
", write_impl_section_heading("Blanket Implementations", "blanket-implementations") )?; - render_impls(cx, &mut w, blanket_impl, containing_item, false)?; + render_impls(cx, &mut w, blanket_impls, containing_item, false)?; w.write_str("
")?; } Ok(()) @@ -1459,10 +1460,10 @@ fn render_assoc_items_inner( ) -> fmt::Result { info!("Documenting associated items of {:?}", containing_item.name); let cache = &cx.shared.cache; - let Some(v) = cache.impls.get(&it) else { return Ok(()) }; - let (mut non_trait, traits): (Vec<_>, _) = - v.iter().partition(|i| i.inner_impl().trait_.is_none()); - if !non_trait.is_empty() { + let Some(impls) = cache.impls.get(&it) else { return Ok(()) }; + let (mut inherent_impls, trait_impls): (Vec<_>, _) = + impls.iter().partition(|i| i.inner_impl().trait_.is_none()); + if !inherent_impls.is_empty() { let render_mode = what.render_mode(); let class_html = what .class() @@ -1485,7 +1486,7 @@ fn render_assoc_items_inner( // we should not show methods from `[MaybeUninit]`. // this `retain` filters out any instances where // the types do not line up perfectly. - non_trait.retain(|impl_| { + inherent_impls.retain(|impl_| { type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache) }); let derived_id = cx.derive_id(&id); @@ -1512,8 +1513,8 @@ fn render_assoc_items_inner( ) } }; - let impls_buf = fmt::from_fn(|f| { - non_trait + let inherent_impls_buf = fmt::from_fn(|f| { + inherent_impls .iter() .map(|i| { render_impl( @@ -1536,10 +1537,10 @@ fn render_assoc_items_inner( }) .to_string(); - if !impls_buf.is_empty() { + if !inherent_impls_buf.is_empty() { write!( w, - "{section_heading}
{impls_buf}
{}", + "{section_heading}
{inherent_impls_buf}
{}", matches!(what, AssocItemRender::DerefFor { .. }) .then_some("") .maybe_display(), @@ -1547,13 +1548,14 @@ fn render_assoc_items_inner( } } - if !traits.is_empty() { - let deref_impl = traits.iter().find(|t| { + if !trait_impls.is_empty() { + let deref_impl = trait_impls.iter().find(|t| { t.trait_did() == cx.tcx().lang_items().deref_trait() && !t.is_negative_trait_impl() }); if let Some(impl_) = deref_impl { - let has_deref_mut = - traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); + let has_deref_mut = trait_impls + .iter() + .any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs)?; } @@ -1563,12 +1565,19 @@ fn render_assoc_items_inner( return Ok(()); } - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, _) = - concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); + let (auto_trait_impls, trait_impls): (Vec<&Impl>, Vec<&Impl>) = + trait_impls.into_iter().partition(|t| t.inner_impl().kind.is_auto()); + let (blanket_impls, concrete_impls): (Vec<&Impl>, _) = + trait_impls.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); - render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl)?; + render_all_impls( + w, + cx, + containing_item, + &concrete_impls, + &auto_trait_impls, + &blanket_impls, + )?; } Ok(()) } diff --git a/tests/rustdoc-html/impl/blanket-impl-trumps-auto-trait-impl.rs b/tests/rustdoc-html/impl/blanket-impl-trumps-auto-trait-impl.rs new file mode 100644 index 0000000000000..6444e87ad18f6 --- /dev/null +++ b/tests/rustdoc-html/impl/blanket-impl-trumps-auto-trait-impl.rs @@ -0,0 +1,26 @@ +// The presence of built-in or user-written trait impls disqualifies potential built-in auto trait +// impls for a type. Here we have a blanket impl of an auto trait which suppresses all hypothetical +// auto trait impls. +// +// Check that we don't claim that there's an auto trait impl & ensure that we show the blanket impl. +// Erroneously we once used to omit the blanket impl since auto trait impls & blanket impls used to +// share the same cache and auto trait impls get generated first. Now, they have separate caches. +// +// issue: + +#![feature(auto_traits)] +#![crate_name = "it"] + +pub auto trait Marker {} + +//@ has 'it/struct.Subject.html' +#[derive(Clone, Copy)] +pub struct Subject; + +// NOTE: `#synthetic-implementations-list` contains auto trait impls only despite its name. +//@ !has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' \ +// 'Marker for Subject' + +//@ has - '//*[@id="blanket-implementations-list"]//*[@class="impl"]' \ +// 'Marker for Twhere T: Copy' +impl Marker for T {}