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] Generic impls #52585

Merged
merged 17 commits into from
Jul 28, 2018
Merged
Show file tree
Hide file tree
Changes from 15 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
185 changes: 147 additions & 38 deletions src/librustdoc/clean/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::traits::auto_trait as auto;
use rustc::ty::TypeFoldable;
use rustc::hir;
use rustc::traits::{self, auto_trait as auto};
use rustc::ty::{self, ToPredicate, TypeFoldable};
use rustc::ty::subst::Subst;
use rustc::infer::InferOk;
use std::fmt::Debug;
use syntax_pos::DUMMY_SP;

use core::DocAccessLevels;

use super::*;

Expand Down Expand Up @@ -75,6 +80,37 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> {
self.get_auto_trait_impls(did, &def_ctor, Some(name))
}

fn get_real_ty<F>(&self,
def_id: DefId,
def_ctor: &F,
real_name: &Option<Ident>,
generics: &ty::Generics,
) -> hir::Ty
where F: Fn(DefId) -> Def {
let path = get_path_for_type(self.cx.tcx, def_id, def_ctor);
let mut segments = path.segments.into_vec();
let last = segments.pop().unwrap();

segments.push(hir::PathSegment::new(
real_name.unwrap_or(last.ident),
self.generics_to_path_params(generics.clone()),
false,
));

let new_path = hir::Path {
span: path.span,
def: path.def,
segments: HirVec::from_vec(segments),
};

hir::Ty {
id: ast::DUMMY_NODE_ID,
node: hir::TyKind::Path(hir::QPath::Resolved(None, P(new_path))),
span: DUMMY_SP,
hir_id: hir::DUMMY_HIR_ID,
}
}

pub fn get_auto_trait_impls<F>(
&self,
def_id: DefId,
Expand All @@ -99,29 +135,124 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> {
let tcx = self.cx.tcx;
let generics = self.cx.tcx.generics_of(def_id);

let ty = self.cx.tcx.type_of(def_id);
let mut traits = Vec::new();
if self.cx.crate_name != Some("core".to_string()) &&
Copy link
Member

Choose a reason for hiding this comment

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

Why does this filter out libcore specifically?

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 panics getting libcore information apparently.

Copy link
Member

Choose a reason for hiding this comment

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

Apparently, at some point since the original implementation, it doesn't panic if you take out this condition any more. I just tried it locally and it made everything just fine.

self.cx.access_levels.borrow().is_doc_reachable(def_id) {
let real_name = name.clone().map(|name| Ident::from_str(&name));
let param_env = self.cx.tcx.param_env(def_id);
for &trait_def_id in self.cx.all_traits.iter() {
if !self.cx.access_levels.borrow().is_doc_reachable(trait_def_id) ||
self.cx.generated_synthetics
.borrow_mut()
.get(&(def_id, trait_def_id))
.is_some() {
continue
}
self.cx.tcx.for_each_relevant_impl(trait_def_id, ty, |impl_def_id| {
self.cx.tcx.infer_ctxt().enter(|infcx| {
let t_generics = infcx.tcx.generics_of(impl_def_id);
let trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap();

match infcx.tcx.type_of(impl_def_id).sty {
::rustc::ty::TypeVariants::TyParam(_) => {},
Copy link
Member

Choose a reason for hiding this comment

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

The typical way to match this is ty::TyParam(_). Also, you want to match on trait_ref.self_ty().sty instead.

_ => return,
}

let substs = infcx.fresh_substs_for_item(DUMMY_SP, def_id);
let ty = ty.subst(infcx.tcx, substs);
let param_env = param_env.subst(infcx.tcx, substs);

let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);

// Require the type the impl is implemented on to match
// our type, and ignore the impl if there was a mismatch.
let cause = traits::ObligationCause::dummy();
let eq_result = infcx.at(&cause, param_env)
.eq(trait_ref.self_ty(), ty);
if let Ok(InferOk { value: (), obligations }) = eq_result {
// FIXME(eddyb) ignoring `obligations` might cause false positives.
drop(obligations);

let may_apply = infcx.predicate_may_hold(&traits::Obligation::new(
cause.clone(),
param_env,
trait_ref.to_predicate(),
));
if !may_apply {
return
}
self.cx.generated_synthetics.borrow_mut()
.insert((def_id, trait_def_id));
let trait_ = hir::TraitRef {
path: get_path_for_type(infcx.tcx,
trait_def_id,
hir::def::Def::Trait),
ref_id: ast::DUMMY_NODE_ID,
};
let provided_trait_methods =
infcx.tcx.provided_trait_methods(trait_def_id)
.into_iter()
.map(|meth| meth.ident.to_string())
.collect();

let ty = self.get_real_ty(def_id, def_ctor, &real_name, generics);
let predicates = infcx.tcx.predicates_of(impl_def_id);

traits.push(Item {
Copy link
Member

Choose a reason for hiding this comment

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

Is there no way to do this? Something like build_impl?

source: infcx.tcx.def_span(impl_def_id).clean(self.cx),
name: None,
attrs: Default::default(),
visibility: None,
def_id: self.next_def_id(impl_def_id.krate),
stability: None,
deprecation: None,
inner: ImplItem(Impl {
unsafety: hir::Unsafety::Normal,
generics: (t_generics, &predicates).clean(self.cx),
provided_trait_methods,
trait_: Some(trait_.clean(self.cx)),
for_: ty.clean(self.cx),
items: infcx.tcx.associated_items(impl_def_id)
.collect::<Vec<_>>()
.clean(self.cx),
polarity: None,
synthetic: false,
blanket_impl: Some(infcx.tcx.type_of(impl_def_id)
.clean(self.cx)),
}),
});
debug!("{:?} => {}", trait_ref, may_apply);
Copy link
Member

Choose a reason for hiding this comment

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

Leftover random debug! (if you want to keep it, you should adjust the message).

}
});
});
}
}

debug!(
"get_auto_trait_impls(def_id={:?}, def_ctor=..., generics={:?}",
def_id, generics
);
let auto_traits: Vec<_> = self.cx
.send_trait
.and_then(|send_trait| {
self.get_auto_trait_impl_for(
def_id,
name.clone(),
generics.clone(),
def_ctor,
send_trait,
)
})
.into_iter()
let auto_traits: Vec<_> =
self.cx.send_trait
Copy link
Contributor

Choose a reason for hiding this comment

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

that's some funky formatting here. revert this block?

Copy link
Member Author

Choose a reason for hiding this comment

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

😎

Copy link
Member

Choose a reason for hiding this comment

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

Just to double-check: i think @oli-obk wanted you to revert this section?

Copy link
Member Author

Choose a reason for hiding this comment

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

Damn I thought I did! >< Might have been lost in my multiple tests...

.and_then(|send_trait| {
self.get_auto_trait_impl_for(
def_id,
name.clone(),
generics.clone(),
def_ctor,
send_trait,
)
}).into_iter()
.chain(self.get_auto_trait_impl_for(
def_id,
name.clone(),
generics.clone(),
def_ctor,
tcx.require_lang_item(lang_items::SyncTraitLangItem),
).into_iter())
.chain(traits.into_iter())
.collect();

debug!(
Expand Down Expand Up @@ -196,31 +327,8 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> {
}
_ => unreachable!(),
};

let path = get_path_for_type(self.cx.tcx, def_id, def_ctor);
let mut segments = path.segments.into_vec();
let last = segments.pop().unwrap();

let real_name = name.map(|name| Ident::from_str(&name));

segments.push(hir::PathSegment::new(
real_name.unwrap_or(last.ident),
self.generics_to_path_params(generics.clone()),
false,
));

let new_path = hir::Path {
span: path.span,
def: path.def,
segments: HirVec::from_vec(segments),
};

let ty = hir::Ty {
id: ast::DUMMY_NODE_ID,
node: hir::TyKind::Path(hir::QPath::Resolved(None, P(new_path))),
span: DUMMY_SP,
hir_id: hir::DUMMY_HIR_ID,
};
let ty = self.get_real_ty(def_id, def_ctor, &real_name, &generics);

return Some(Item {
source: Span::empty(),
Expand All @@ -239,6 +347,7 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> {
items: Vec::new(),
polarity,
synthetic: true,
blanket_impl: None,
}),
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec<clean:
if auto_traits {
let auto_impls = get_auto_traits_with_def_id(cx, did);
let mut renderinfo = cx.renderinfo.borrow_mut();

let new_impls: Vec<clean::Item> = auto_impls.into_iter()
.filter(|i| renderinfo.inlined.insert(i.def_id)).collect();

Expand Down Expand Up @@ -415,6 +414,7 @@ pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
items: trait_items,
polarity: Some(polarity.clean(cx)),
synthetic: false,
blanket_impl: None,
}),
source: tcx.def_span(did).clean(cx),
name: None,
Expand Down
3 changes: 2 additions & 1 deletion src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,6 @@ impl GenericBound {
}

fn get_trait_type(&self) -> Option<Type> {

if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self {
return Some(trait_.clone());
}
Expand Down Expand Up @@ -3882,6 +3881,7 @@ pub struct Impl {
pub items: Vec<Item>,
pub polarity: Option<ImplPolarity>,
pub synthetic: bool,
pub blanket_impl: Option<Type>,
}

pub fn get_auto_traits_with_node_id(cx: &DocContext, id: ast::NodeId, name: String) -> Vec<Item> {
Expand Down Expand Up @@ -3949,6 +3949,7 @@ impl Clean<Vec<Item>> for doctree::Impl {
items,
polarity: Some(self.polarity.clean(cx)),
synthetic: false,
blanket_impl: None,
})
});
ret
Expand Down
4 changes: 3 additions & 1 deletion src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use rustc_lint;
use rustc_driver::{self, driver, target_features, abort_on_err};
use rustc::session::{self, config};
use rustc::hir::def_id::{DefId, CrateNum};
use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
use rustc::hir::def::Def;
use rustc::middle::cstore::CrateStore;
use rustc::middle::privacy::AccessLevels;
Expand Down Expand Up @@ -84,6 +84,7 @@ pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> {
/// Maps (type_id, trait_id) -> auto trait impl
pub generated_synthetics: RefCell<FxHashSet<(DefId, DefId)>>,
pub current_item_name: RefCell<Option<Name>>,
pub all_traits: Vec<DefId>,
}

impl<'a, 'tcx, 'rcx> DocContext<'a, 'tcx, 'rcx> {
Expand Down Expand Up @@ -385,6 +386,7 @@ pub fn run_core(search_paths: SearchPaths,
all_fake_def_ids: RefCell::new(FxHashSet()),
generated_synthetics: RefCell::new(FxHashSet()),
current_item_name: RefCell::new(None),
all_traits: tcx.all_traits(LOCAL_CRATE).to_vec(),
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we can filter this down to just the ones with blanket impls? That may require some overhead here but would also cut down on the traits we iterate over when gathering impls.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure to understand you. I do this call here because it otherwise would need to be done on every item (and also because @eddyb thinks it's better this way, which I agree).

Copy link
Member

Choose a reason for hiding this comment

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

Right, i understand why you wanted to cache the traits. I'm asking if it's possible to store less traits in this, by filtering out traits that don't have a blanket/generic impl.

};
debug!("crate: {:?}", tcx.hir.krate());

Expand Down
6 changes: 5 additions & 1 deletion src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,11 @@ fn fmt_impl(i: &clean::Impl,
write!(f, " for ")?;
}

fmt_type(&i.for_, f, use_absolute)?;
if let Some(ref ty) = i.blanket_impl {
fmt_type(ty, f, use_absolute)?;
} else {
fmt_type(&i.for_, f, use_absolute)?;
}

fmt::Display::fmt(&WhereClause { gens: &i.generics, indent: 0, end_newline: true }, f)?;
Ok(())
Expand Down
Loading