-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
[rustdoc] Generic impls #52585
Changes from 15 commits
54ea8eb
39849d5
ccdf4ae
73cb823
e8cca55
2bc7c03
3e96ac1
6b830ec
d64c2ac
8fa6e3f
bcb5c6f
459550b
6eb0b05
ef7d6fc
7a3c7b2
bdbb5b9
06364bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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::*; | ||
|
||
|
@@ -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, | ||
|
@@ -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()) && | ||
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(_) => {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The typical way to match this is |
||
_ => 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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there no way to do this? Something like |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leftover random |
||
} | ||
}); | ||
}); | ||
} | ||
} | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's some funky formatting here. revert this block? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😎 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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!( | ||
|
@@ -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(), | ||
|
@@ -239,6 +347,7 @@ impl<'a, 'tcx, 'rcx> AutoTraitFinder<'a, 'tcx, 'rcx> { | |
items: Vec::new(), | ||
polarity, | ||
synthetic: true, | ||
blanket_impl: None, | ||
}), | ||
}); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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> { | ||
|
@@ -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(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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). There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()); | ||
|
||
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.There was a problem hiding this comment.
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.