Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rustdoc] List matching impls on type aliases #112429

Merged
merged 3 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`]: #method.cast_mut
/// [`cast_mut`]: pointer::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`](#method.from_bits-1).
/// The inverse method is [`from_bits`](pointer#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`](#method.to_bits-1).
/// The inverse method is [`to_bits`](pointer#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`]: #method.as_uninit_ref-1
/// [`as_uninit_ref`]: pointer#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`]: #method.as_ref-1
/// [`as_ref`]: pointer#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`]: #method.as_ref-1
/// [`as_ref`]: pointer#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`]: #method.as_uninit_ref-1
/// [`as_uninit_ref`]: pointer#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`]: #method.offset-1
/// [`offset`]: pointer#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`]: #method.as_ref-1
/// [`as_ref`]: pointer#method.as_ref-1
/// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut
///
/// # Safety
Expand Down
90 changes: 78 additions & 12 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,15 @@ 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::TyCtxt;
use rustc_middle::ty::{ParamEnv, 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 @@ -1112,28 +1115,76 @@ 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);
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs, aliased_type);
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 {
GuillaumeGomez marked this conversation as resolved.
Show resolved Hide resolved
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>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure threading this parameter through all these function calls is probably cheaper than using let is_alias = tcx.def_kind(it) == DefKind::TyAlias and using let aliased_type = tcx.type_of(it) to resolve it, since the calling functions already have this information.

But is pulling the information out of TyCtxt expensive enough to be worth having a function with seven parameters?

Copy link
Member Author

@GuillaumeGomez GuillaumeGomez Jun 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a variable to tell us that this is an alias. From this, since we already have the DefId, why not using it directly?

EDIT: Just re-read your comment. I'm not sure to like it much simply because we re-compute an information we already have. But that's pretty much my only argument against it. ^^'

Copy link
Member Author

@GuillaumeGomez GuillaumeGomez Jun 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I edited my comment, I don't know how but I misread your comment the first time. Sorry about that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's not a lot of computation going into it, though. It's looking information up in a hash table.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the part worrying me but since I can't put the finger on it, I'll just remove the newly added argument and let's see how it goes. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah found it! I need it because of Some(aliased_type) => cache.impls.get(&aliased_type).unwrap_or(&empty),. We can't retrieve the DefId easily from a Ty. The only method I found to get it is ty_adt_id but it doesn't cover all the cases we need.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Thanks for the info!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having aliased_type separately here feels off to me and may at least deserve a comment, but that's unrelated to the type system itself so I am fine with that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment for aliased_type is already on the function. ;)

) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
let cache = &shared.cache;
let Some(v) = cache.impls.get(&it) else { return };
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
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();
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
Expand Down Expand Up @@ -1165,6 +1216,12 @@ 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 @@ -1193,9 +1250,14 @@ 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 {
if let Some(impl_) = deref_impl &&
(!is_alias || is_valid_impl_for(tcx, it, impl_.def_id()))
{
let has_deref_mut =
traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
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()))
});
render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
}

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

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 (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());

render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
Expand Down Expand Up @@ -1247,10 +1313,10 @@ fn render_deref_methods(
return;
}
}
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
} 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);
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
}
}
}
Expand Down
48 changes: 32 additions & 16 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>(
display_fn(move |f| {
let (item, mut cx) = templ.item_and_mut_cx();
let def_id = item.item_id.expect_def_id();
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All);
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
write!(f, "{v}")
})
}
Expand Down Expand Up @@ -893,7 +893,11 @@ 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));
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
);

let cloned_shared = Rc::clone(&cx.shared);
let cache = &cloned_shared.cache;
Expand Down Expand Up @@ -1125,8 +1129,12 @@ 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))
.unwrap();
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
)
.unwrap();
}

fn item_opaque_ty(
Expand Down Expand Up @@ -1154,8 +1162,12 @@ 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))
.unwrap();
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
)
.unwrap();
}

fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
Expand All @@ -1179,11 +1191,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();
// 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,
"{}",
render_assoc_items(cx, it, def_id, AssocItemRender::All, t.type_.def_id(&cx.cache()))
);
write!(w, "{}", document_type_layout(cx, def_id));
}

Expand Down Expand Up @@ -1423,7 +1435,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));
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
write!(w, "{}", document_type_layout(cx, def_id));
}

Expand Down Expand Up @@ -1466,7 +1478,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)).unwrap();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)).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 @@ -1571,7 +1583,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));
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
write!(w, "{}", document_type_layout(cx, def_id));
}

Expand Down Expand Up @@ -1606,8 +1618,12 @@ 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))
.unwrap();
write!(
w,
"{}",
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
)
.unwrap();
}

fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
Expand Down
Loading