rustdoc: refactor(?) synthetic impl building.

This commit is contained in:
Eduard-Mihai Burtescu 2019-04-22 22:52:51 +03:00
parent c835607907
commit be9f43e0d4
5 changed files with 106 additions and 199 deletions

View File

@ -44,7 +44,6 @@ impl<A> AutoTraitResult<A> {
pub struct AutoTraitInfo<'cx> {
pub full_user_env: ty::ParamEnv<'cx>,
pub region_data: RegionConstraintData<'cx>,
pub names_map: FxHashSet<String>,
pub vid_to_region: FxHashMap<ty::RegionVid, ty::Region<'cx>>,
}
@ -78,15 +77,12 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
pub fn find_auto_trait_generics<A>(
&self,
ty: Ty<'tcx>,
param_env_def_id: DefId,
orig_env: ty::ParamEnv<'tcx>,
trait_did: DefId,
generics: &ty::Generics,
auto_trait_callback: impl for<'i> Fn(&InferCtxt<'_, 'tcx, 'i>, AutoTraitInfo<'i>) -> A,
) -> AutoTraitResult<A> {
let tcx = self.tcx;
let orig_params = tcx.param_env(param_env_def_id);
let trait_ref = ty::TraitRef {
def_id: trait_did,
substs: tcx.mk_substs_trait(ty, &[]),
@ -98,16 +94,16 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let mut selcx = SelectionContext::with_negative(&infcx, true);
let result = selcx.select(&Obligation::new(
ObligationCause::dummy(),
orig_params,
orig_env,
trait_pred.to_poly_trait_predicate(),
));
match result {
Ok(Some(Vtable::VtableImpl(_))) => {
debug!(
"find_auto_trait_generics(ty={:?}, trait_did={:?}, generics={:?}): \
"find_auto_trait_generics({:?}): \
manual impl found, bailing out",
ty, trait_did, generics
trait_ref
);
true
}
@ -160,8 +156,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
&mut infcx,
trait_did,
ty,
orig_params.clone(),
orig_params,
orig_env,
orig_env,
&mut fresh_preds,
false,
) {
@ -173,21 +169,21 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
&mut infcx,
trait_did,
ty,
new_env.clone(),
new_env,
user_env,
&mut fresh_preds,
true,
).unwrap_or_else(|| {
panic!(
"Failed to fully process: {:?} {:?} {:?}",
ty, trait_did, orig_params
ty, trait_did, orig_env
)
});
debug!(
"find_auto_trait_generics(ty={:?}, trait_did={:?}, generics={:?}): fulfilling \
"find_auto_trait_generics({:?}): fulfilling \
with {:?}",
ty, trait_did, generics, full_env
trait_ref, full_env
);
infcx.clear_caches();
@ -209,15 +205,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
)
});
let names_map: FxHashSet<String> = generics
.params
.iter()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => Some(param.name.to_string()),
_ => None,
})
.collect();
let body_id_map: FxHashMap<_, _> = infcx
.region_obligations
.borrow()
@ -225,7 +212,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
.map(|&(id, _)| (id, vec![]))
.collect();
infcx.process_registered_region_obligations(&body_id_map, None, full_env.clone());
infcx.process_registered_region_obligations(&body_id_map, None, full_env);
let region_data = infcx
.borrow_region_constraints()
@ -237,7 +224,6 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let info = AutoTraitInfo {
full_user_env,
region_data,
names_map,
vid_to_region,
};
@ -284,7 +270,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// the final synthesized generics: we don't want our generated docs page to contain something
// like 'T: Copy + Clone', as that's redundant. Therefore, we keep track of a separate
// 'user_env', which only holds the predicates that will actually be displayed to the user.
pub fn evaluate_predicates<'b, 'gcx, 'c>(
fn evaluate_predicates<'b, 'gcx, 'c>(
&self,
infcx: &InferCtxt<'b, 'tcx, 'c>,
trait_did: DefId,
@ -311,13 +297,13 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let mut user_computed_preds: FxHashSet<_> =
user_env.caller_bounds.iter().cloned().collect();
let mut new_env = param_env.clone();
let mut new_env = param_env;
let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID);
while let Some(pred) = predicates.pop_front() {
infcx.clear_caches();
if !already_visited.insert(pred.clone()) {
if !already_visited.insert(pred) {
continue;
}
@ -365,7 +351,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
already_visited.remove(&pred);
self.add_user_pred(
&mut user_computed_preds,
ty::Predicate::Trait(pred.clone()),
ty::Predicate::Trait(pred),
);
predicates.push_back(pred);
} else {
@ -384,7 +370,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
computed_preds.extend(user_computed_preds.iter().cloned());
let normalized_preds =
elaborate_predicates(tcx, computed_preds.clone().into_iter().collect());
elaborate_predicates(tcx, computed_preds.iter().cloned().collect());
new_env = ty::ParamEnv::new(
tcx.mk_predicates(normalized_preds),
param_env.reveal,
@ -519,28 +505,9 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
}
pub fn region_name(&self, region: Region<'_>) -> Option<String> {
match region {
&ty::ReEarlyBound(r) => Some(r.name.to_string()),
_ => None,
}
}
pub fn get_lifetime(&self, region: Region<'_>,
names_map: &FxHashMap<String, String>) -> String {
self.region_name(region)
.map(|name|
names_map.get(&name).unwrap_or_else(||
panic!("Missing lifetime with name {:?} for {:?}", name, region)
)
)
.cloned()
.unwrap_or_else(|| "'static".to_owned())
}
// This is very similar to handle_lifetimes. However, instead of matching ty::Region's
// to each other, we match ty::RegionVid's to ty::Region's
pub fn map_vid_to_region<'cx>(
fn map_vid_to_region<'cx>(
&self,
regions: &RegionConstraintData<'cx>,
) -> FxHashMap<ty::RegionVid, ty::Region<'cx>> {
@ -650,7 +617,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
}
pub fn evaluate_nested_obligations<
fn evaluate_nested_obligations<
'b,
'c,
'd,
@ -669,10 +636,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let dummy_cause = ObligationCause::misc(DUMMY_SP, hir::DUMMY_HIR_ID);
for (obligation, mut predicate) in nested
.map(|o| (o.clone(), o.predicate.clone()))
.map(|o| (o.clone(), o.predicate))
{
let is_new_pred =
fresh_preds.insert(self.clean_pred(select.infcx(), predicate.clone()));
fresh_preds.insert(self.clean_pred(select.infcx(), predicate));
// Resolve any inference variables that we can, to help selection succeed
predicate = select.infcx().resolve_type_vars_if_possible(&predicate);
@ -690,14 +657,14 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// We check this by calling is_of_param on the relevant types
// from the various possible predicates
match &predicate {
&ty::Predicate::Trait(ref p) => {
&ty::Predicate::Trait(p) => {
if self.is_param_no_infer(p.skip_binder().trait_ref.substs)
&& !only_projections
&& is_new_pred {
self.add_user_pred(computed_preds, predicate);
}
predicates.push_back(p.clone());
predicates.push_back(p);
}
&ty::Predicate::Projection(p) => {
debug!("evaluate_nested_obligations: examining projection predicate {:?}",
@ -739,7 +706,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
if p.ty().skip_binder().has_infer_types() {
debug!("Projecting and unifying projection predicate {:?}",
predicate);
match poly_project_and_unify_type(select, &obligation.with(p.clone())) {
match poly_project_and_unify_type(select, &obligation.with(p)) {
Err(e) => {
debug!(
"evaluate_nested_obligations: Unable to unify predicate \

View File

@ -1,5 +1,5 @@
use rustc::hir;
use rustc::traits::auto_trait as auto;
use rustc::traits::auto_trait::{self, AutoTraitResult};
use rustc::ty::{self, TypeFoldable};
use std::fmt::Debug;
@ -7,12 +7,12 @@ use super::*;
pub struct AutoTraitFinder<'a, 'tcx> {
pub cx: &'a core::DocContext<'tcx>,
pub f: auto::AutoTraitFinder<'a, 'tcx>,
pub f: auto_trait::AutoTraitFinder<'a, 'tcx>,
}
impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
pub fn new(cx: &'a core::DocContext<'tcx>) -> Self {
let f = auto::AutoTraitFinder::new(cx.tcx);
let f = auto_trait::AutoTraitFinder::new(cx.tcx);
AutoTraitFinder { cx, f }
}
@ -24,68 +24,66 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
ty: Ty<'tcx>,
param_env_def_id: DefId,
) -> Vec<Item> {
let generics = self.cx.tcx.generics_of(param_env_def_id);
let param_env = self.cx.tcx.param_env(param_env_def_id);
debug!(
"get_auto_trait_impls(param_env_def_id={:?}, generics={:?}",
param_env_def_id, generics
debug!("get_auto_trait_impls({:?})", ty);
let auto_traits = self.cx.send_trait.into_iter().chain(
Some(self.cx.tcx.require_lang_item(lang_items::SyncTraitLangItem))
);
let auto_traits: Vec<_> = self.cx
.send_trait
.and_then(|send_trait| {
self.get_auto_trait_impl_for(
ty,
param_env_def_id,
generics,
send_trait,
)
})
.into_iter()
.chain(self.get_auto_trait_impl_for(
ty,
param_env_def_id,
generics,
self.cx.tcx.require_lang_item(lang_items::SyncTraitLangItem),
).into_iter())
.collect();
debug!(
"get_auto_traits: type {:?} auto_traits {:?}",
param_env_def_id, auto_traits
);
auto_traits
}
fn get_auto_trait_impl_for(
&self,
ty: Ty<'tcx>,
param_env_def_id: DefId,
generics: &ty::Generics,
trait_def_id: DefId,
) -> Option<Item> {
if !self.cx
.generated_synthetics
.borrow_mut()
.insert((param_env_def_id, trait_def_id))
{
debug!(
"get_auto_trait_impl_for(param_env_def_id={:?}, generics={:?}, \
trait_def_id={:?}): already generated, aborting",
param_env_def_id, generics, trait_def_id
);
return None;
}
let result = self.find_auto_trait_generics(ty, param_env_def_id, trait_def_id, &generics);
if result.is_auto() {
auto_traits.filter_map(|trait_def_id| {
let trait_ref = ty::TraitRef {
def_id: trait_def_id,
substs: self.cx.tcx.mk_substs_trait(ty, &[]),
};
if !self.cx
.generated_synthetics
.borrow_mut()
.insert((ty, trait_def_id))
{
debug!(
"get_auto_trait_impl_for({:?}): already generated, aborting",
trait_ref
);
return None;
}
let result = self.f.find_auto_trait_generics(
ty,
param_env,
trait_def_id,
|infcx, info| {
let region_data = info.region_data;
let names_map = self.cx.tcx.generics_of(param_env_def_id)
.params
.iter()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => Some(param.name.to_string()),
_ => None,
})
.map(|name| (name.clone(), Lifetime(name)))
.collect();
let lifetime_predicates =
self.handle_lifetimes(&region_data, &names_map);
let new_generics = self.param_env_to_generics(
infcx.tcx,
param_env_def_id,
info.full_user_env,
lifetime_predicates,
info.vid_to_region,
);
debug!(
"find_auto_trait_generics(param_env_def_id={:?}, trait_def_id={:?}): \
finished with {:?}",
param_env_def_id, trait_def_id, new_generics
);
new_generics
},
);
let polarity;
let new_generics = match result {
AutoTraitResult::PositiveImpl(new_generics) => {
polarity = None;
@ -106,21 +104,18 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// Instead, we generate `impl !Send for Foo<T>`, which better
// expresses the fact that `Foo<T>` never implements `Send`,
// regardless of the choice of `T`.
let real_generics = (generics, &Default::default());
// Clean the generics, but ignore the '?Sized' bounds generated
// by the `Clean` impl
let clean_generics = real_generics.clean(self.cx);
let params = (self.cx.tcx.generics_of(param_env_def_id), &Default::default())
.clean(self.cx).params;
Generics {
params: clean_generics.params,
params,
where_predicates: Vec::new(),
}
}
_ => unreachable!(),
AutoTraitResult::ExplicitImpl => return None,
};
return Some(Item {
Some(Item {
source: Span::empty(),
name: None,
attrs: Default::default(),
@ -139,49 +134,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
synthetic: true,
blanket_impl: None,
}),
});
}
None
}
fn find_auto_trait_generics(
&self,
ty: Ty<'tcx>,
param_env_def_id: DefId,
trait_did: DefId,
generics: &ty::Generics,
) -> AutoTraitResult {
match self.f.find_auto_trait_generics(ty, param_env_def_id, trait_did, generics,
|infcx, mut info| {
let region_data = info.region_data;
let names_map =
info.names_map
.drain()
.map(|name| (name.clone(), Lifetime(name)))
.collect();
let lifetime_predicates =
self.handle_lifetimes(&region_data, &names_map);
let new_generics = self.param_env_to_generics(
infcx.tcx,
param_env_def_id,
info.full_user_env,
generics,
lifetime_predicates,
info.vid_to_region,
);
debug!(
"find_auto_trait_generics(ty={:?}, trait_did={:?}, generics={:?}): \
finished with {:?}",
ty, trait_did, generics, new_generics
);
new_generics
}) {
auto::AutoTraitResult::ExplicitImpl => AutoTraitResult::ExplicitImpl,
auto::AutoTraitResult::NegativeImpl => AutoTraitResult::NegativeImpl,
auto::AutoTraitResult::PositiveImpl(res) => AutoTraitResult::PositiveImpl(res),
}
})
}).collect()
}
fn get_lifetime(
@ -497,14 +451,13 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
tcx: TyCtxt<'b, 'c, 'cx>,
param_env_def_id: DefId,
param_env: ty::ParamEnv<'cx>,
type_generics: &ty::Generics,
mut existing_predicates: Vec<WherePredicate>,
vid_to_region: FxHashMap<ty::RegionVid, ty::Region<'cx>>,
) -> Generics {
debug!(
"param_env_to_generics(param_env_def_id={:?}, param_env={:?}, type_generics={:?}, \
"param_env_to_generics(param_env_def_id={:?}, param_env={:?}, \
existing_predicates={:?})",
param_env_def_id, param_env, type_generics, existing_predicates
param_env_def_id, param_env, existing_predicates
);
// The `Sized` trait must be handled specially, since we only display it when
@ -534,11 +487,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
(replaced.clone(), replaced.clean(self.cx))
});
let full_generics = (type_generics, &tcx.explicit_predicates_of(param_env_def_id));
let Generics {
params: mut generic_params,
..
} = full_generics.clean(self.cx);
let mut generic_params = (
tcx.generics_of(param_env_def_id),
&tcx.explicit_predicates_of(param_env_def_id),
).clean(self.cx).params;
let mut has_sized = FxHashSet::default();
let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default();

View File

@ -25,23 +25,23 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
ty: Ty<'tcx>,
param_env_def_id: DefId,
) -> Vec<Item> {
debug!("get_blanket_impls(param_env_def_id={:?}, ...)", param_env_def_id);
let mut impls = Vec::new();
let param_env = self.cx.tcx.param_env(param_env_def_id);
debug!("get_blanket_impls({:?})", ty);
let mut impls = Vec::new();
for &trait_def_id in self.cx.all_traits.iter() {
if !self.cx.renderinfo.borrow().access_levels.is_doc_reachable(trait_def_id) ||
self.cx.generated_synthetics
.borrow_mut()
.get(&(param_env_def_id, trait_def_id))
.get(&(ty, trait_def_id))
.is_some() {
continue
}
self.cx.tcx.for_each_relevant_impl(trait_def_id, ty, |impl_def_id| {
debug!("get_blanket_impls: Considering impl for trait '{:?}' {:?}",
trait_def_id, impl_def_id);
let trait_ref = self.cx.tcx.impl_trait_ref(impl_def_id).unwrap();
let may_apply = self.cx.tcx.infer_ctxt().enter(|infcx| {
debug!("get_blanket_impls: Considering impl for trait '{:?}' {:?}",
trait_def_id, impl_def_id);
match trait_ref.self_ty().sty {
ty::Param(_) => {},
_ => return false,
@ -84,12 +84,12 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
debug!("get_blanket_impls: found applicable impl: {}\
for trait_ref={:?}, ty={:?}",
may_apply, trait_ref, ty);
if !may_apply {
return
return;
}
self.cx.generated_synthetics.borrow_mut()
.insert((param_env_def_id, trait_def_id));
.insert((ty, trait_def_id));
let provided_trait_methods =
self.cx.tcx.provided_trait_methods(trait_def_id)
.into_iter()
@ -111,6 +111,8 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
&self.cx.tcx.explicit_predicates_of(impl_def_id),
).clean(self.cx),
provided_trait_methods,
// FIXME(eddyb) compute both `trait_` and `for_` from
// the post-inference `trait_ref`, as it's more accurate.
trait_: Some(trait_ref.clean(self.cx).get_trait_type().unwrap()),
for_: ty.clean(self.cx),
items: self.cx.tcx.associated_items(impl_def_id)

View File

@ -4452,21 +4452,6 @@ enum SimpleBound {
Outlives(Lifetime),
}
enum AutoTraitResult {
ExplicitImpl,
PositiveImpl(Generics),
NegativeImpl,
}
impl AutoTraitResult {
fn is_auto(&self) -> bool {
match *self {
AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl => true,
_ => false,
}
}
}
impl From<GenericBound> for SimpleBound {
fn from(bound: GenericBound) -> Self {
match bound.clone() {

View File

@ -5,7 +5,7 @@ use rustc::hir::def::Def;
use rustc::hir::HirId;
use rustc::middle::cstore::CrateStore;
use rustc::middle::privacy::AccessLevels;
use rustc::ty::TyCtxt;
use rustc::ty::{Ty, TyCtxt};
use rustc::lint::{self, LintPass};
use rustc::session::config::ErrorOutputType;
use rustc::session::DiagnosticOutput;
@ -70,8 +70,9 @@ pub struct DocContext<'tcx> {
pub send_trait: Option<DefId>,
pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
/// Maps (type_id, trait_id) -> auto trait impl
pub generated_synthetics: RefCell<FxHashSet<(DefId, DefId)>>,
/// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
// FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
pub all_traits: Vec<DefId>,
}