Skip to content

Commit 19edb3c

Browse files
committed
rustdoc: list matching impls on type aliases
Remake of "List matching impls on type aliases" * 4b1d13d * 6f552c8 * 2ce7cd9 Partially reverts "Fix infinite loop when retrieving impls for type alias", but keeps the test case. This version of the PR avoids the infinite loop by structurally matching types instead of using full unification. This version does not support type alias trait bounds, but the compiler does not enforce those anyway (#21903).
1 parent 58eefc3 commit 19edb3c

File tree

2 files changed

+102
-4
lines changed

2 files changed

+102
-4
lines changed

src/librustdoc/html/render/mod.rs

+43-4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5454
use rustc_hir::def_id::{DefId, DefIdSet};
5555
use rustc_hir::Mutability;
5656
use rustc_middle::middle::stability;
57+
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
5758
use rustc_middle::ty::TyCtxt;
5859
use rustc_span::{
5960
symbol::{sym, Symbol},
@@ -62,6 +63,7 @@ use rustc_span::{
6263
use serde::ser::{SerializeMap, SerializeSeq};
6364
use serde::{Serialize, Serializer};
6465

66+
use crate::clean::types::TypeAliasItem;
6567
use crate::clean::{self, ItemId, RenderedLink, SelfTy};
6668
use crate::error::Error;
6769
use crate::formats::cache::Cache;
@@ -1139,8 +1141,40 @@ fn render_assoc_items_inner(
11391141
info!("Documenting associated items of {:?}", containing_item.name);
11401142
let shared = Rc::clone(&cx.shared);
11411143
let cache = &shared.cache;
1142-
let Some(v) = cache.impls.get(&it) else { return };
1143-
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
1144+
let tcx = cx.tcx();
1145+
let av = if let TypeAliasItem(ait) = &*containing_item.kind &&
1146+
let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
1147+
let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
1148+
let Some(mut av) = cache.impls.get(&aliased_type_defid).cloned() &&
1149+
let Some(alias_def_id) = containing_item.item_id.as_def_id()
1150+
{
1151+
// This branch of the compiler compares types structually, but does
1152+
// not check trait bounds. That's probably fine, since type aliases
1153+
// don't normally constrain on them anyway.
1154+
// https://github.com/rust-lang/rust/issues/21903
1155+
//
1156+
// If that changes, then this will need to check them with type
1157+
// unification.
1158+
let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
1159+
let reject_cx = DeepRejectCtxt {
1160+
treat_obligation_params: TreatParams::AsCandidateKey,
1161+
};
1162+
av.retain(|impl_| {
1163+
if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
1164+
reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
1165+
} else {
1166+
false
1167+
}
1168+
});
1169+
av
1170+
} else {
1171+
Vec::new()
1172+
};
1173+
let blank = Vec::new();
1174+
let v = cache.impls.get(&it).unwrap_or(&blank);
1175+
let (non_trait, traits): (Vec<_>, _) =
1176+
v.iter().chain(&av[..]).partition(|i| i.inner_impl().trait_.is_none());
1177+
let mut saw_impls = FxHashSet::default();
11441178
if !non_trait.is_empty() {
11451179
let mut tmp_buf = Buffer::html();
11461180
let (render_mode, id, class_html) = match what {
@@ -1169,6 +1203,9 @@ fn render_assoc_items_inner(
11691203
};
11701204
let mut impls_buf = Buffer::html();
11711205
for i in &non_trait {
1206+
if !saw_impls.insert(i.def_id()) {
1207+
continue;
1208+
}
11721209
render_impl(
11731210
&mut impls_buf,
11741211
cx,
@@ -1214,8 +1251,10 @@ fn render_assoc_items_inner(
12141251

12151252
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
12161253
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1217-
let (blanket_impl, concrete): (Vec<&Impl>, _) =
1218-
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1254+
let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
1255+
.into_iter()
1256+
.filter(|t| saw_impls.insert(t.def_id()))
1257+
.partition(|t| t.inner_impl().kind.is_blanket());
12191258

12201259
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
12211260
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Regression test for <https://github.com/rust-lang/rust/issues/32077>.
2+
3+
#![crate_name = "foo"]
4+
5+
pub struct GenericStruct<T>(T);
6+
7+
impl<T> GenericStruct<T> {
8+
pub fn on_gen(arg: T) {}
9+
}
10+
11+
impl GenericStruct<u32> {
12+
pub fn on_u32(arg: u32) {}
13+
}
14+
15+
pub trait Foo {}
16+
pub trait Bar {}
17+
18+
impl<T> Foo for GenericStruct<T> {}
19+
impl Bar for GenericStruct<u32> {}
20+
21+
// @has 'foo/type.TypedefStruct.html'
22+
// We check that we have the implementation of the type alias itself.
23+
// @has - '//*[@id="impl-TypedefStruct"]/h3' 'impl TypedefStruct'
24+
// @has - '//*[@id="method.on_alias"]/h4' 'pub fn on_alias()'
25+
// @has - '//*[@id="impl-GenericStruct%3CT%3E"]/h3' 'impl<T> GenericStruct<T>'
26+
// @has - '//*[@id="method.on_gen"]/h4' 'pub fn on_gen(arg: T)'
27+
// @has - '//*[@id="impl-Foo-for-GenericStruct%3CT%3E"]/h3' 'impl<T> Foo for GenericStruct<T>'
28+
// This trait implementation doesn't match the type alias parameters so shouldn't appear in docs.
29+
// @!has - '//h3' 'impl Bar for GenericStruct<u32> {}'
30+
// Same goes for the `Deref` impl.
31+
// @!has - '//h2' 'Methods from Deref<Target = u32>'
32+
pub type TypedefStruct = GenericStruct<u8>;
33+
34+
impl TypedefStruct {
35+
pub fn on_alias() {}
36+
}
37+
38+
impl std::ops::Deref for GenericStruct<u32> {
39+
type Target = u32;
40+
41+
fn deref(&self) -> &Self::Target {
42+
&self.0
43+
}
44+
}
45+
46+
pub struct Wrap<T>(GenericStruct<T>);
47+
48+
// @has 'foo/type.Alias.html'
49+
// @has - '//h2' 'Methods from Deref<Target = u32>'
50+
// @has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl<T> Deref for Wrap<T>'
51+
pub type Alias = Wrap<u32>;
52+
53+
impl<T> std::ops::Deref for Wrap<T> {
54+
type Target = GenericStruct<T>;
55+
56+
fn deref(&self) -> &Self::Target {
57+
&self.0
58+
}
59+
}

0 commit comments

Comments
 (0)