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

Support GATs in Rustdoc #94009

Merged
merged 5 commits into from Mar 4, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/librustdoc/clean/auto_trait.rs
Expand Up @@ -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| {
Expand All @@ -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 },
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/clean/inline.rs
Expand Up @@ -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),
Expand Down
161 changes: 117 additions & 44 deletions src/librustdoc/clean/mod.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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((
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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)
}
};

Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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_,
Expand All @@ -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_,
Expand Down Expand Up @@ -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) },
});
}
Expand Down Expand Up @@ -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),
},
Expand Down Expand Up @@ -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),
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/clean/simplify.rs
Expand Up @@ -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| {
Expand All @@ -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() },
});
}
Expand Down
20 changes: 10 additions & 10 deletions src/librustdoc/clean/types.rs
Expand Up @@ -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),
Expand Down Expand Up @@ -721,7 +721,7 @@ impl ItemKind {
| ProcMacroItem(_)
| PrimitiveItem(_)
| AssocConstItem(_, _)
| AssocTypeItem(_, _)
| AssocTypeItem(..)
| StrippedItem(_)
| KeywordItem(_) => [].iter(),
}
Expand Down Expand Up @@ -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].
///
Expand All @@ -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.
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/fold.rs
Expand Up @@ -86,7 +86,7 @@ crate trait DocFolder: Sized {
| ProcMacroItem(_)
| PrimitiveItem(_)
| AssocConstItem(_, _)
| AssocTypeItem(_, _)
| AssocTypeItem(..)
| KeywordItem(_) => kind,
}
}
Expand Down