Skip to content

Commit

Permalink
Auto merge of #112543 - GuillaumeGomez:revert-112429, r=lcnr
Browse files Browse the repository at this point in the history
[rustdoc] Fix infinite loop when retrieving impls for type alias

Fixes #112515.
Reverts #112429.

r? `@lcnr`
  • Loading branch information
bors committed Jun 12, 2023
2 parents fd0a331 + b93ca01 commit cb882fa
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 178 deletions.
18 changes: 9 additions & 9 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ impl<T: ?Sized> *mut T {
/// with [`cast_mut`] on `*const T` and may have documentation value if used instead of implicit
/// coercion.
///
/// [`cast_mut`]: pointer::cast_mut
/// [`cast_mut`]: #method.cast_mut
#[stable(feature = "ptr_const_cast", since = "1.65.0")]
#[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
#[inline(always)]
Expand All @@ -117,7 +117,7 @@ impl<T: ?Sized> *mut T {
/// Casts a pointer to its raw bits.
///
/// This is equivalent to `as usize`, but is more specific to enhance readability.
/// The inverse method is [`from_bits`](pointer#method.from_bits-1).
/// The inverse method is [`from_bits`](#method.from_bits-1).
///
/// In particular, `*p as usize` and `p as usize` will both compile for
/// pointers to numeric types but do very different things, so using this
Expand Down Expand Up @@ -153,7 +153,7 @@ impl<T: ?Sized> *mut T {
/// Creates a pointer from its raw bits.
///
/// This is equivalent to `as *mut T`, but is more specific to enhance readability.
/// The inverse method is [`to_bits`](pointer#method.to_bits-1).
/// The inverse method is [`to_bits`](#method.to_bits-1).
///
/// # Examples
///
Expand Down Expand Up @@ -303,7 +303,7 @@ impl<T: ?Sized> *mut T {
///
/// For the mutable counterpart see [`as_mut`].
///
/// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1
/// [`as_uninit_ref`]: #method.as_uninit_ref-1
/// [`as_mut`]: #method.as_mut
///
/// # Safety
Expand Down Expand Up @@ -369,7 +369,7 @@ impl<T: ?Sized> *mut T {
///
/// For the mutable counterpart see [`as_uninit_mut`].
///
/// [`as_ref`]: pointer#method.as_ref-1
/// [`as_ref`]: #method.as_ref-1
/// [`as_uninit_mut`]: #method.as_uninit_mut
///
/// # Safety
Expand Down Expand Up @@ -624,7 +624,7 @@ impl<T: ?Sized> *mut T {
/// For the shared counterpart see [`as_ref`].
///
/// [`as_uninit_mut`]: #method.as_uninit_mut
/// [`as_ref`]: pointer#method.as_ref-1
/// [`as_ref`]: #method.as_ref-1
///
/// # Safety
///
Expand Down Expand Up @@ -689,7 +689,7 @@ impl<T: ?Sized> *mut T {
/// For the shared counterpart see [`as_uninit_ref`].
///
/// [`as_mut`]: #method.as_mut
/// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1
/// [`as_uninit_ref`]: #method.as_uninit_ref-1
///
/// # Safety
///
Expand Down Expand Up @@ -779,7 +779,7 @@ impl<T: ?Sized> *mut T {
///
/// This function is the inverse of [`offset`].
///
/// [`offset`]: pointer#method.offset-1
/// [`offset`]: #method.offset-1
///
/// # Safety
///
Expand Down Expand Up @@ -2051,7 +2051,7 @@ impl<T> *mut [T] {
///
/// For the mutable counterpart see [`as_uninit_slice_mut`].
///
/// [`as_ref`]: pointer#method.as_ref-1
/// [`as_ref`]: #method.as_ref-1
/// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut
///
/// # Safety
Expand Down
90 changes: 12 additions & 78 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,12 @@ use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_middle::middle::stability;
use rustc_middle::ty::{ParamEnv, TyCtxt};
use rustc_middle::ty::TyCtxt;
use rustc_span::{
symbol::{sym, Symbol},
BytePos, FileName, RealFileName,
};
use rustc_trait_selection::traits::ObligationCtxt;
use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Serialize, Serializer};

Expand Down Expand Up @@ -1115,76 +1112,28 @@ fn render_assoc_items<'a, 'cx: 'a>(
containing_item: &'a clean::Item,
it: DefId,
what: AssocItemRender<'a>,
aliased_type: Option<DefId>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
derefs.insert(it);
display_fn(move |f| {
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs, aliased_type);
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
Ok(())
})
}

/// Check whether `impl_def_id` may apply to *some instantiation* of `item_def_id`.
fn is_valid_impl_for(tcx: TyCtxt<'_>, item_def_id: DefId, impl_def_id: DefId) -> bool {
let infcx = tcx.infer_ctxt().intercrate(true).build();
let ocx = ObligationCtxt::new(&infcx);
let param_env = ParamEnv::empty();

let alias_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, item_def_id);
let alias_ty = tcx.type_of(item_def_id).subst(tcx, alias_substs);
let alias_bounds = tcx.predicates_of(item_def_id).instantiate(tcx, alias_substs);

let impl_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, impl_def_id);
let impl_self_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs);
let impl_bounds = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);

if ocx.eq(&ObligationCause::dummy(), param_env, impl_self_ty, alias_ty).is_err() {
return false;
}
ocx.register_obligations(
alias_bounds
.iter()
.chain(impl_bounds)
.map(|(p, _)| Obligation::new(tcx, ObligationCause::dummy(), param_env, p)),
);

let errors = ocx.select_where_possible();
errors.is_empty()
}

// If `aliased_type` is `Some`, it means `it` is a type alias and `aliased_type` is the "actual"
// type aliased behind `it`. It is used to check whether or not the implementation of the aliased
// type can be displayed on the alias doc page.
fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
it: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
aliased_type: Option<DefId>,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
let cache = &shared.cache;
let empty = Vec::new();
let v = match cache.impls.get(&it) {
Some(v) => v,
None => &empty,
};
let v2 = match aliased_type {
Some(aliased_type) => cache.impls.get(&aliased_type).unwrap_or(&empty),
None => &empty,
};
if v.is_empty() && v2.is_empty() {
return;
}
let mut saw_impls = FxHashSet::default();
let (non_trait, traits): (Vec<_>, _) =
v.iter().chain(v2).partition(|i| i.inner_impl().trait_.is_none());
let tcx = cx.tcx();
let is_alias = aliased_type.is_some();
let Some(v) = cache.impls.get(&it) else { return };
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
Expand Down Expand Up @@ -1216,12 +1165,6 @@ fn render_assoc_items_inner(
};
let mut impls_buf = Buffer::html();
for i in &non_trait {
if !saw_impls.insert(i.def_id()) {
continue;
}
if is_alias && !is_valid_impl_for(tcx, it, i.def_id()) {
continue;
}
render_impl(
&mut impls_buf,
cx,
Expand Down Expand Up @@ -1250,14 +1193,9 @@ fn render_assoc_items_inner(
if !traits.is_empty() {
let deref_impl =
traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
if let Some(impl_) = deref_impl &&
(!is_alias || is_valid_impl_for(tcx, it, impl_.def_id()))
{
if let Some(impl_) = deref_impl {
let has_deref_mut =
traits.iter().any(|t| {
t.trait_did() == cx.tcx().lang_items().deref_mut_trait() &&
(!is_alias || is_valid_impl_for(tcx, it, t.def_id()))
});
traits.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);
}

Expand All @@ -1267,14 +1205,10 @@ fn render_assoc_items_inner(
return;
}

let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = traits
.into_iter()
.filter(|t| saw_impls.insert(t.def_id()))
.partition(|t| t.inner_impl().kind.is_auto());
let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
.into_iter()
.filter(|t| !is_alias || is_valid_impl_for(tcx, it, t.def_id()))
.partition(|t| t.inner_impl().kind.is_blanket());
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());

render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
Expand Down Expand Up @@ -1313,10 +1247,10 @@ fn render_deref_methods(
return;
}
}
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
} else if let Some(prim) = target.primitive_type() {
if let Some(&did) = cache.primitive_locations.get(&prim) {
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
}
}
}
Expand Down
48 changes: 16 additions & 32 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ macro_rules! item_template_methods {
display_fn(move |f| {
let (item, mut cx) = self.item_and_mut_cx();
let def_id = item.item_id.expect_def_id();
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All);
write!(f, "{v}")
})
}
Expand Down Expand Up @@ -953,11 +953,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}

// If there are methods directly on this trait object, render them here.
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
);
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All));

let cloned_shared = Rc::clone(&cx.shared);
let cache = &cloned_shared.cache;
Expand Down Expand Up @@ -1189,12 +1185,8 @@ fn item_trait_alias(
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
)
.unwrap();
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
.unwrap();
}

fn item_opaque_ty(
Expand Down Expand Up @@ -1222,12 +1214,8 @@ fn item_opaque_ty(
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
)
.unwrap();
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
.unwrap();
}

fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
Expand All @@ -1251,11 +1239,11 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));

let def_id = it.item_id.expect_def_id();
write!(
w,
"{}",
render_assoc_items(cx, it, def_id, AssocItemRender::All, t.type_.def_id(&cx.cache()))
);
// Render any items associated directly to this alias, as otherwise they
// won't be visible anywhere in the docs. It would be nice to also show
// associated items from the aliased type (see discussion in #32077), but
// we need #14072 to make sense of the generics.
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
}

Expand Down Expand Up @@ -1494,7 +1482,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
write!(w, "</div>");
}
let def_id = it.item_id.expect_def_id();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
}

Expand Down Expand Up @@ -1537,7 +1525,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite
let def_id = it.item_id.expect_def_id();
write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)).unwrap();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)).unwrap();
} else {
// We handle the "reference" primitive type on its own because we only want to list
// implementations on generic types.
Expand Down Expand Up @@ -1642,7 +1630,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
}
}
let def_id = it.item_id.expect_def_id();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
}

Expand Down Expand Up @@ -1677,12 +1665,8 @@ fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::
});

write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
)
.unwrap();
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
.unwrap();
}

fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
Expand Down
30 changes: 30 additions & 0 deletions tests/rustdoc/issue-112515-impl-ty-alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Regression test for <https://github.com/rust-lang/rust/issues/112515>.
// It's to ensure that this code doesn't have infinite loop in rustdoc when
// trying to retrive type alias implementations.

// ignore-tidy-linelength

pub type Boom = S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<u64, u8>, ()>, ()>, ()>, u8>, ()>, u8>, ()>, u8>, u8>, ()>, ()>, ()>, u8>, u8>, u8>, ()>, ()>, u8>, ()>, ()>, ()>, u8>, u8>, ()>, ()>, ()>, ()>, ()>, u8>, ()>, ()>, u8>, ()>, ()>, ()>, u8>, ()>, ()>, u8>, u8>, u8>, u8>, ()>, u8>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>, ()>;
pub struct S<T, U>(T, U);

pub trait A {}

pub trait B<T> {
type P;
}

impl A for u64 {}

impl<T, U> A for S<T, U> {}

impl<T> B<u8> for S<T, ()>
where
T: B<u8>,
<T as B<u8>>::P: A,
{
type P = ();
}

impl<T: A, U, V> B<T> for S<U, V> {
type P = ();
}
Loading

0 comments on commit cb882fa

Please sign in to comment.