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: re-sugar more cross-crate trait bounds #102439

Merged
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
9 changes: 9 additions & 0 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,15 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
}

if let ty::TraitContainer = assoc_item.container {
// FIXME(fmease): `tcx.explicit_item_bounds` does not contain the bounds of GATs,
// e.g. the bounds `Copy`, `Display` & (implicitly) `Sized` in
// `type Assoc<T: Copy> where T: Display`. This also means that we
// later incorrectly render `where T: ?Sized`.
//
// The result of `tcx.explicit_predicates_of` *does* contain them but
// it does not contain the other bounds / predicates we need.
// Either merge those two interned lists somehow or refactor
// `clean_ty_generics` to call `explicit_item_bounds` by itself.
let bounds = tcx.explicit_item_bounds(assoc_item.def_id);
let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
let mut generics =
Expand Down
39 changes: 16 additions & 23 deletions src/librustdoc/clean/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::DefId;
use rustc_middle::ty;
use rustc_span::Symbol;

use crate::clean;
use crate::clean::GenericArgs as PP;
Expand All @@ -26,21 +25,17 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
//
// We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
// the order of the generated bounds.
let mut params: FxIndexMap<Symbol, (Vec<_>, Vec<_>)> = FxIndexMap::default();
let mut tybounds = FxIndexMap::default();
let mut lifetimes = Vec::new();
let mut equalities = Vec::new();
let mut tybounds = Vec::new();

for clause in clauses {
match clause {
WP::BoundPredicate { ty, bounds, bound_params } => match ty {
clean::Generic(s) => {
let (b, p) = params.entry(s).or_default();
b.extend(bounds);
p.extend(bound_params);
}
t => tybounds.push((t, (bounds, bound_params))),
},
WP::BoundPredicate { ty, bounds, bound_params } => {
let (b, p): &mut (Vec<_>, Vec<_>) = tybounds.entry(ty).or_default();
b.extend(bounds);
p.extend(bound_params);
}
WP::RegionPredicate { lifetime, bounds } => {
lifetimes.push((lifetime, bounds));
}
Expand All @@ -49,14 +44,17 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
}

// Look for equality predicates on associated types that can be merged into
// general bound predicates
// general bound predicates.
equalities.retain(|&(ref lhs, ref rhs)| {
let Some((self_, trait_did, name)) = lhs.projection() else {
return true;
};
let clean::Generic(generic) = self_ else { return true };
let Some((bounds, _)) = params.get_mut(generic) else { return true };

let Some((ty, trait_did, name)) = lhs.projection() else { return true; };
// FIXME(fmease): We don't handle HRTBs correctly here.
// Pass `_bound_params` (higher-rank lifetimes) to a modified version of
// `merge_bounds`. That vector is currently always empty though since we
// don't keep track of late-bound lifetimes when cleaning projection
// predicates to cleaned equality predicates while we should first query
// them with `collect_referenced_late_bound_regions` and then store them
// (or something similar). For prior art, see `clean::auto_trait`.
let Some((bounds, _bound_params)) = tybounds.get_mut(ty) else { return true };
merge_bounds(cx, bounds, trait_did, name, rhs)
});

Expand All @@ -65,11 +63,6 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
clauses.extend(
lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }),
);
clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate {
ty: clean::Generic(k),
bounds,
bound_params: params,
}));
clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate {
ty,
bounds,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h4 class="code-header">type <a href="#associatedtype.Out0" class="associatedtype">Out0</a>: <a class="trait" href="../assoc_item_trait_bounds_with_bindings/trait.Support.html" title="trait assoc_item_trait_bounds_with_bindings::Support">Support</a>&lt;Item = <a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>&gt;</h4>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h4 class="code-header">type <a href="#associatedtype.Out9" class="associatedtype">Out9</a>: <a class="trait" href="{{channel}}/core/ops/function/trait.FnMut.html" title="trait core::ops::function::FnMut">FnMut</a>(<a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>) -&gt; <a class="primitive" href="{{channel}}/std/primitive.bool.html">bool</a> + <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></h4>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Regression test for issues #77763, #84579 and #102142.
#![crate_name = "main"]

// aux-build:assoc_item_trait_bounds_with_bindings.rs
// build-aux-docs
// ignore-cross-compile
extern crate assoc_item_trait_bounds_with_bindings as aux;

// FIXME(fmease): Don't render an incorrect `T: ?Sized` where-clause for parameters
// of GATs like `Main::Out{2,4}`. Add a snapshot test once it's fixed.
// FIXME(fmease): Print the `for<>` parameter list in the bounds of
// `Main::Out{6,11,12}`.

// @has main/trait.Main.html
// @has - '//*[@id="associatedtype.Out0"]' 'type Out0: Support<Item = ()>'
// @has - '//*[@id="associatedtype.Out1"]' 'type Out1: Support<Item = Self::Item>'
// @has - '//*[@id="associatedtype.Out2"]' 'type Out2<T>: Support<Item = T>'
// @has - '//*[@id="associatedtype.Out3"]' 'type Out3: Support<Produce<()> = bool>'
// @has - '//*[@id="associatedtype.Out4"]' 'type Out4<T>: Support<Produce<T> = T>'
// @has - '//*[@id="associatedtype.Out5"]' "type Out5: Support<Output<'static> = &'static ()>"
// @has - '//*[@id="associatedtype.Out6"]' "type Out6: Support<Output<'a> = &'a ()>"
// @has - '//*[@id="associatedtype.Out7"]' "type Out7: Support<Item = String, Produce<i32> = u32> + Unrelated"
// @has - '//*[@id="associatedtype.Out8"]' "type Out8: Unrelated + Protocol<i16, Q1 = u128, Q0 = ()>"
// @has - '//*[@id="associatedtype.Out9"]' "type Out9: FnMut(i32) -> bool + Clone"
// @has - '//*[@id="associatedtype.Out10"]' "type Out10<'q>: Support<Output<'q> = ()>"
// @has - '//*[@id="associatedtype.Out11"]' "type Out11: Helper<A<'s> = &'s (), B<'r> = ()>"
// @has - '//*[@id="associatedtype.Out12"]' "type Out12: Helper<B<'w> = Cow<'w, str>, A<'w> = bool>"
//
// Snapshots: Check that we do not render any where-clauses for those associated types since all of
// the trait bounds contained within were moved to the bounds of the respective item.
//
// @snapshot out0 - '//*[@id="associatedtype.Out0"]/*[@class="code-header"]'
// @snapshot out9 - '//*[@id="associatedtype.Out9"]/*[@class="code-header"]'
//
// @has - '//*[@id="tymethod.make"]' \
// "fn make<F>(F, impl FnMut(&str) -> bool)\
// where \
// F: FnOnce(u32) -> String, \
// Self::Out2<()>: Protocol<u8, Q0 = Self::Item, Q1 = ()>"
pub use aux::Main;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
pub trait Main {
type Item;

type Out0: Support<Item = ()>;
type Out1: Support<Item = Self::Item>;
type Out2<T>: Support<Item = T>;
type Out3: Support<Produce<()> = bool>;
type Out4<T>: Support<Produce<T> = T>;
type Out5: Support<Output<'static> = &'static ()>;
type Out6: for<'a> Support<Output<'a> = &'a ()>;
type Out7: Support<Item = String, Produce<i32> = u32> + Unrelated;
type Out8: Unrelated + Protocol<i16, Q1 = u128, Q0 = ()>;
type Out9: FnMut(i32) -> bool + Clone;
type Out10<'q>: Support<Output<'q> = ()>;
type Out11: for<'r, 's> Helper<A<'s> = &'s (), B<'r> = ()>;
type Out12: for<'w> Helper<B<'w> = std::borrow::Cow<'w, str>, A<'w> = bool>;

fn make<F>(_: F, _: impl FnMut(&str) -> bool)
where
F: FnOnce(u32) -> String,
Self::Out2<()>: Protocol<u8, Q0 = Self::Item, Q1 = ()>;
}

pub trait Support {
type Item;
type Output<'a>;
type Produce<T>;
}

pub trait Protocol<K> {
type Q0;
type Q1;
}

pub trait Unrelated {}

pub trait Helper {
type A<'q>;
type B<'q>;
}