-
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 11 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,33 @@ 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 +131,126 @@ 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()) && | ||
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. Why does this filter out libcore specifically? 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. It panics getting 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. 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) { | ||
if let ty::TyAdt(_adt, _) = ty.sty { | ||
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. why are we only doing this for adts? wouldn't it be practical to also see impls on tuples, array, slices, ...? 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. True! But the answer lies on @eddyb side here. Or at least I recall him asking me to only perform this on |
||
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) || | ||
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 think it would make sense to prefilter 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. This is something I discussed a bit earlier with @QuietMisdreavus so I'll do it as well. 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. Actually I can't, it depends on the context so we have to make this check in here ( |
||
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 generics = infcx.tcx.generics_of(impl_def_id); | ||
let trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap(); | ||
|
||
if !match infcx.tcx.type_of(impl_def_id).sty { | ||
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. match infcx.tcx.type_of(impl_def_id).sty {
::rustc::ty::TyParam(_) => {},
_ => return,
} 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. Thanks! |
||
::rustc::ty::TypeVariants::TyParam(_) => true, | ||
_ => false, | ||
} { | ||
return | ||
} | ||
|
||
let substs = infcx.fresh_substs_for_item(DUMMY_SP, def_id); | ||
let ty2 = ty.subst(infcx.tcx, substs); | ||
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. we don't need both 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. We do need
|
||
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(), ty2); | ||
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() | ||
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. couldn't we do this way earlier (like where we're early aborting usually) 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. No because a trait can have multiple generic impls and we only know at this point that the current impl matches. |
||
.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); | ||
|
||
traits.push(Item { | ||
source: Span::empty(), | ||
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: (generics, | ||
&tcx.predicates_of(impl_def_id)).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: true, | ||
}), | ||
}); | ||
debug!("{:?} => {}", trait_ref, may_apply); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
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 +325,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(), | ||
|
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()); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
#![crate_name = "foo"] | ||
|
||
use std::fmt; | ||
|
||
// @!has foo/struct.Bar.html '//h3[@id="impl-ToString"]//code' 'impl<T> ToString for Bar' | ||
pub struct Bar; | ||
|
||
// @has foo/struct.Foo.html '//h3[@id="impl-ToString"]//code' 'impl<T> ToString for Foo' | ||
pub struct Foo; | ||
|
||
impl fmt::Display for Foo { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "Foo") | ||
} | ||
} |
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.
nit: this function signature should follow rustfmt block indent style (especially since it looks like the function below already follows that