Skip to content

Support GATs in Rustdoc #94009

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

Merged
merged 5 commits into from
Mar 4, 2022
Merged
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
6 changes: 3 additions & 3 deletions src/librustdoc/clean/auto_trait.rs
Original file line number Diff line number Diff line change
@@ -546,11 +546,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
WherePredicate::EqPredicate { lhs, rhs } => {
match lhs {
Type::QPath { name: left_name, ref self_type, ref trait_, .. } => {
Type::QPath { ref assoc, ref self_type, ref trait_, .. } => {
let ty = &*self_type;
let mut new_trait = trait_.clone();

if self.is_fn_trait(trait_) && left_name == sym::Output {
if self.is_fn_trait(trait_) && assoc.name == sym::Output {
ty_to_fn
.entry(*ty.clone())
.and_modify(|e| {
@@ -571,7 +571,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// to 'T: Iterator<Item=u8>'
GenericArgs::AngleBracketed { ref mut bindings, .. } => {
bindings.push(TypeBinding {
name: left_name,
assoc: *assoc.clone(),
kind: TypeBindingKind::Equality { term: rhs },
});
}
2 changes: 1 addition & 1 deletion src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
@@ -636,7 +636,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:

g.where_predicates.retain(|pred| match pred {
clean::WherePredicate::BoundPredicate {
ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, name: _, .. },
ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, .. },
bounds,
..
} => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),
161 changes: 117 additions & 44 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
@@ -388,14 +388,35 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
let trait_ = lifted.trait_ref(cx.tcx).clean(cx);
let self_type = self.self_ty().clean(cx);
Type::QPath {
name: cx.tcx.associated_item(self.item_def_id).name,
assoc: Box::new(projection_to_path_segment(*self, cx)),
self_def_id: self_type.def_id(&cx.cache),
self_type: box self_type,
trait_,
}
}
}

fn projection_to_path_segment(ty: ty::ProjectionTy<'_>, cx: &mut DocContext<'_>) -> PathSegment {
let item = cx.tcx.associated_item(ty.item_def_id);
let generics = cx.tcx.generics_of(ty.item_def_id);
PathSegment {
name: item.name,
args: GenericArgs::AngleBracketed {
args: ty.substs[generics.parent_count..]
.iter()
.map(|ty| match ty.unpack() {
ty::subst::GenericArgKind::Lifetime(lt) => {
GenericArg::Lifetime(lt.clean(cx).unwrap())
}
ty::subst::GenericArgKind::Type(ty) => GenericArg::Type(ty.clean(cx)),
ty::subst::GenericArgKind::Const(c) => GenericArg::Const(Box::new(c.clean(cx))),
})
.collect(),
bindings: Default::default(),
},
}
}

impl Clean<GenericParamDef> for ty::GenericParamDef {
fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef {
let (name, kind) = match self.kind {
@@ -601,8 +622,8 @@ fn clean_ty_generics(
})
.collect::<Vec<GenericParamDef>>();

// param index -> [(DefId of trait, associated type name, type)]
let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, Symbol, Ty<'_>)>>::default();
// param index -> [(DefId of trait, associated type name and generics, type)]
let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>)>>::default();

let where_predicates = preds
.predicates
@@ -648,8 +669,9 @@ fn clean_ty_generics(

let proj = projection
.map(|p| (p.skip_binder().projection_ty.clean(cx), p.skip_binder().term));
if let Some(((_, trait_did, name), rhs)) =
proj.as_ref().and_then(|(lhs, rhs)| Some((lhs.projection()?, rhs)))
if let Some(((_, trait_did, name), rhs)) = proj
.as_ref()
.and_then(|(lhs, rhs): &(Type, _)| Some((lhs.projection()?, rhs)))
{
// FIXME(...): Remove this unwrap()
impl_trait_proj.entry(param_idx).or_default().push((
@@ -985,9 +1007,10 @@ impl Clean<Item> for hir::TraitItem<'_> {
TyMethodItem(t)
}
hir::TraitItemKind::Type(bounds, ref default) => {
let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
let default = default.map(|t| t.clean(cx));
AssocTypeItem(bounds, default)
AssocTypeItem(Box::new(generics), bounds, default)
}
};
let what_rustc_thinks =
@@ -1019,15 +1042,9 @@ impl Clean<Item> for hir::ImplItem<'_> {
}
hir::ImplItemKind::TyAlias(ref hir_ty) => {
let type_ = hir_ty.clean(cx);
let generics = self.generics.clean(cx);
let item_type = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx);
TypedefItem(
Typedef {
type_,
generics: Generics::default(),
item_type: Some(item_type),
},
true,
)
TypedefItem(Typedef { type_, generics, item_type: Some(item_type) }, true)
}
};

@@ -1135,35 +1152,79 @@ impl Clean<Item> for ty::AssocItem {
ty::AssocKind::Type => {
let my_name = self.name;

fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool {
match (&param.kind, arg) {
(GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty)))
if *ty == param.name =>
{
true
}
(
GenericParamDefKind::Lifetime { .. },
GenericArg::Lifetime(Lifetime(lt)),
) if *lt == param.name => true,
(GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => {
match &c.kind {
ConstantKind::TyConst { expr } => expr == param.name.as_str(),
_ => false,
}
}
_ => false,
}
}

if let ty::TraitContainer(_) = self.container {
let bounds = tcx.explicit_item_bounds(self.def_id);
let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
let generics = clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
let mut generics =
clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
// Filter out the bounds that are (likely?) directly attached to the associated type,
// as opposed to being located in the where clause.
let mut bounds = generics
.where_predicates
.iter()
.filter_map(|pred| {
let (name, self_type, trait_, bounds) = match *pred {
WherePredicate::BoundPredicate {
ty: QPath { ref name, ref self_type, ref trait_, .. },
ref bounds,
..
} => (name, self_type, trait_, bounds),
_ => return None,
};
if *name != my_name {
return None;
}
if trait_.def_id() != self.container.id() {
return None;
.drain_filter(|pred| match *pred {
WherePredicate::BoundPredicate {
ty: QPath { ref assoc, ref self_type, ref trait_, .. },
..
} => {
if assoc.name != my_name {
return false;
}
if trait_.def_id() != self.container.id() {
return false;
}
match **self_type {
Generic(ref s) if *s == kw::SelfUpper => {}
_ => return false,
}
match &assoc.args {
GenericArgs::AngleBracketed { args, bindings } => {
if !bindings.is_empty()
|| generics
.params
.iter()
.zip(args)
.any(|(param, arg)| !param_eq_arg(param, arg))
{
return false;
}
}
GenericArgs::Parenthesized { .. } => {
// The only time this happens is if we're inside the rustdoc for Fn(),
// which only has one associated type, which is not a GAT, so whatever.
}
}
true
}
match **self_type {
Generic(ref s) if *s == kw::SelfUpper => {}
_ => return None,
_ => false,
})
.flat_map(|pred| {
if let WherePredicate::BoundPredicate { bounds, .. } = pred {
bounds
} else {
unreachable!()
}
Some(bounds)
})
.flat_map(|i| i.iter().cloned())
.collect::<Vec<_>>();
// Our Sized/?Sized bound didn't get handled when creating the generics
// because we didn't actually get our whole set of bounds until just now
@@ -1183,7 +1244,7 @@ impl Clean<Item> for ty::AssocItem {
None
};

AssocTypeItem(bounds, ty.map(|t| t.clean(cx)))
AssocTypeItem(Box::new(generics), bounds, ty.map(|t| t.clean(cx)))
} else {
// FIXME: when could this happen? Associated items in inherent impls?
let type_ = tcx.type_of(self.def_id).clean(cx);
@@ -1243,7 +1304,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
};
register_res(cx, trait_.res);
Type::QPath {
name: p.segments.last().expect("segments were empty").ident.name,
assoc: Box::new(p.segments.last().expect("segments were empty").clean(cx)),
self_def_id: Some(DefId::local(qself.hir_id.owner.local_def_index)),
self_type: box qself.clean(cx),
trait_,
@@ -1260,7 +1321,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
let trait_ = hir::Path { span, res, segments: &[] }.clean(cx);
register_res(cx, trait_.res);
Type::QPath {
name: segment.ident.name,
assoc: Box::new(segment.clean(cx)),
self_def_id: res.opt_def_id(),
self_type: box qself.clean(cx),
trait_,
@@ -1533,7 +1594,16 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
let mut bindings = vec![];
for pb in obj.projection_bounds() {
bindings.push(TypeBinding {
name: cx.tcx.associated_item(pb.item_def_id()).name,
assoc: projection_to_path_segment(
pb.skip_binder()
.lift_to_tcx(cx.tcx)
.unwrap()
// HACK(compiler-errors): Doesn't actually matter what self
// type we put here, because we're only using the GAT's substs.
.with_self_ty(cx.tcx, cx.tcx.types.self_param)
.projection_ty,
cx,
),
kind: TypeBindingKind::Equality { term: pb.skip_binder().term.clean(cx) },
});
}
@@ -1599,10 +1669,10 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
== trait_ref.skip_binder()
{
Some(TypeBinding {
name: cx
.tcx
.associated_item(proj.projection_ty.item_def_id)
.name,
assoc: projection_to_path_segment(
proj.projection_ty,
cx,
),
kind: TypeBindingKind::Equality {
term: proj.term.clean(cx),
},
@@ -2145,7 +2215,10 @@ fn clean_maybe_renamed_foreign_item(

impl Clean<TypeBinding> for hir::TypeBinding<'_> {
fn clean(&self, cx: &mut DocContext<'_>) -> TypeBinding {
TypeBinding { name: self.ident.name, kind: self.kind.clean(cx) }
TypeBinding {
assoc: PathSegment { name: self.ident.name, args: self.gen_args.clean(cx) },
kind: self.kind.clean(cx),
}
}
}

4 changes: 2 additions & 2 deletions src/librustdoc/clean/simplify.rs
Original file line number Diff line number Diff line change
@@ -89,7 +89,7 @@ crate fn merge_bounds(
cx: &clean::DocContext<'_>,
bounds: &mut Vec<clean::GenericBound>,
trait_did: DefId,
name: Symbol,
assoc: clean::PathSegment,
rhs: &clean::Term,
) -> bool {
!bounds.iter_mut().any(|b| {
@@ -107,7 +107,7 @@ crate fn merge_bounds(
match last.args {
PP::AngleBracketed { ref mut bindings, .. } => {
bindings.push(clean::TypeBinding {
name,
assoc: assoc.clone(),
kind: clean::TypeBindingKind::Equality { term: rhs.clone() },
});
}
20 changes: 10 additions & 10 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
@@ -683,7 +683,7 @@ crate enum ItemKind {
///
/// The bounds may be non-empty if there is a `where` clause.
/// The `Option<Type>` is the default concrete type (e.g. `trait Trait { type Target = usize; }`)
AssocTypeItem(Vec<GenericBound>, Option<Type>),
AssocTypeItem(Box<Generics>, Vec<GenericBound>, Option<Type>),
/// An item that has been stripped by a rustdoc pass
StrippedItem(Box<ItemKind>),
KeywordItem(Symbol),
@@ -721,7 +721,7 @@ impl ItemKind {
| ProcMacroItem(_)
| PrimitiveItem(_)
| AssocConstItem(_, _)
| AssocTypeItem(_, _)
| AssocTypeItem(..)
| StrippedItem(_)
| KeywordItem(_) => [].iter(),
}
@@ -1397,7 +1397,7 @@ crate enum Type {

/// A qualified path to an associated item: `<Type as Trait>::Name`
QPath {
name: Symbol,
assoc: Box<PathSegment>,
self_type: Box<Type>,
/// FIXME: This is a hack that should be removed; see [this discussion][1].
///
@@ -1415,7 +1415,7 @@ crate enum Type {

// `Type` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Type, 72);
rustc_data_structures::static_assert_size!(Type, 80);

impl Type {
/// When comparing types for equality, it can help to ignore `&` wrapping.
@@ -1505,12 +1505,12 @@ impl Type {
self.primitive_type().is_some()
}

crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> {
let (self_, trait_, name) = match self {
QPath { self_type, trait_, name, .. } => (self_type, trait_, name),
crate fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
let (self_, trait_, assoc) = match self {
QPath { self_type, trait_, assoc, .. } => (self_type, trait_, assoc),
_ => return None,
};
Some((&self_, trait_.def_id(), *name))
Some((&self_, trait_.def_id(), *assoc.clone()))
}

fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
@@ -2018,7 +2018,7 @@ crate enum GenericArg {
// `GenericArg` can occur many times in a single `Path`, so make sure it
// doesn't increase in size unexpectedly.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(GenericArg, 80);
rustc_data_structures::static_assert_size!(GenericArg, 88);

#[derive(Clone, PartialEq, Eq, Debug, Hash)]
crate enum GenericArgs {
@@ -2256,7 +2256,7 @@ crate struct ProcMacro {
/// `A: Send + Sync` in `Foo<A: Send + Sync>`).
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
crate struct TypeBinding {
crate name: Symbol,
crate assoc: PathSegment,
crate kind: TypeBindingKind,
}

2 changes: 1 addition & 1 deletion src/librustdoc/fold.rs
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ crate trait DocFolder: Sized {
| ProcMacroItem(_)
| PrimitiveItem(_)
| AssocConstItem(_, _)
| AssocTypeItem(_, _)
| AssocTypeItem(..)
| KeywordItem(_) => kind,
}
}
Loading