Reformatted and commented various bits of the auto trait machinery.

This commit is contained in:
Inokentiy Babushkin 2018-04-07 00:25:25 +02:00
parent 0a4a85e8ae
commit 309e4035aa
No known key found for this signature in database
GPG key ID: 7EFC8EC5224DE8EC

View file

@ -25,13 +25,13 @@ use ty::fold::TypeFolder;
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub enum RegionTarget<'tcx> {
Region(Region<'tcx>),
RegionVid(RegionVid)
RegionVid(RegionVid),
}
#[derive(Default, Debug, Clone)]
pub struct RegionDeps<'tcx> {
larger: FxHashSet<RegionTarget<'tcx>>,
smaller: FxHashSet<RegionTarget<'tcx>>
smaller: FxHashSet<RegionTarget<'tcx>>,
}
pub enum AutoTraitResult<A> {
@ -43,7 +43,8 @@ pub enum AutoTraitResult<A> {
impl<A> AutoTraitResult<A> {
fn is_auto(&self) -> bool {
match *self {
AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl => true,
AutoTraitResult::PositiveImpl(_) |
AutoTraitResult::NegativeImpl => true,
_ => false,
}
}
@ -65,14 +66,17 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
AutoTraitFinder { tcx }
}
pub fn find_auto_trait_generics<A>(
&self,
did: DefId,
trait_did: DefId,
generics: &ty::Generics,
auto_trait_callback: impl for<'i> Fn(&InferCtxt<'_, 'tcx, 'i>, AutoTraitInfo<'i>) -> A)
-> AutoTraitResult<A>
{
/// Determine whether an auto trait is implemented for a type, and if this is the case if
/// non-trivial bounds need to be fulfilled, invoke a callback to compute a value representing
/// these in a fashion suitable for the caller.
pub fn find_auto_trait_generics<A>(&self,
did: DefId,
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 ty = self.tcx.type_of(did);
@ -85,32 +89,33 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let trait_pred = ty::Binder::bind(trait_ref);
let bail_out = tcx.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::with_negative(&infcx, true);
let result = selcx.select(&Obligation::new(
ObligationCause::dummy(),
orig_params,
trait_pred.to_poly_trait_predicate(),
));
match result {
Ok(Some(Vtable::VtableImpl(_))) => {
debug!(
"find_auto_trait_generics(did={:?}, trait_did={:?}, generics={:?}): \
let bail_out = tcx.infer_ctxt()
.enter(|infcx| {
let mut selcx = SelectionContext::with_negative(&infcx, true);
let result =
selcx.select(&Obligation::new(ObligationCause::dummy(),
orig_params,
trait_pred.to_poly_trait_predicate()));
match result {
Ok(Some(Vtable::VtableImpl(_))) => {
debug!("find_auto_trait_generics(did={:?}, trait_did={:?}, generics={:?}): \
manual impl found, bailing out",
did, trait_did, generics
);
return true;
}
_ => return false,
};
});
did,
trait_did,
generics);
return true;
}
_ => return false,
};
});
// If an explicit impl exists, it always takes priority over an auto impl
if bail_out {
return AutoTraitResult::ExplicitImpl;
}
return tcx.infer_ctxt().enter(|mut infcx| {
return tcx.infer_ctxt()
.enter(|mut infcx| {
let mut fresh_preds = FxHashSet();
// Due to the way projections are handled by SelectionContext, we need to run
@ -146,60 +151,59 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing
// SelectionContext to return it back to us.
let (new_env, user_env) = match self.evaluate_predicates(
&mut infcx,
did,
trait_did,
ty,
orig_params.clone(),
orig_params,
&mut fresh_preds,
false,
) {
Some(e) => e,
None => return AutoTraitResult::NegativeImpl,
};
let (new_env, user_env) =
match self.evaluate_predicates(&mut infcx,
did,
trait_did,
ty,
orig_params.clone(),
orig_params,
&mut fresh_preds,
false) {
Some(e) => e,
None => return AutoTraitResult::NegativeImpl,
};
let (full_env, full_user_env) = self.evaluate_predicates(
&mut infcx,
did,
trait_did,
ty,
new_env.clone(),
user_env,
&mut fresh_preds,
true,
).unwrap_or_else(|| {
panic!(
"Failed to fully process: {:?} {:?} {:?}",
ty, trait_did, orig_params
)
});
let (full_env, full_user_env) = self.evaluate_predicates(&mut infcx,
did,
trait_did,
ty,
new_env.clone(),
user_env,
&mut fresh_preds,
true)
.unwrap_or_else(|| {
panic!("Failed to fully process: {:?} {:?} {:?}",
ty,
trait_did,
orig_params)
});
debug!(
"find_auto_trait_generics(did={:?}, trait_did={:?}, generics={:?}): fulfilling \
debug!("find_auto_trait_generics(did={:?}, trait_did={:?}, generics={:?}): fulfilling \
with {:?}",
did, trait_did, generics, full_env
);
did,
trait_did,
generics,
full_env);
infcx.clear_caches();
// At this point, we already have all of the bounds we need. FulfillmentContext is used
// to store all of the necessary region/lifetime bounds in the InferContext, as well as
// an additional sanity check.
let mut fulfill = FulfillmentContext::new();
fulfill.register_bound(
&infcx,
full_env,
ty,
trait_did,
ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID),
);
fulfill.select_all_or_error(&infcx).unwrap_or_else(|e| {
panic!(
"Unable to fulfill trait {:?} for '{:?}': {:?}",
trait_did, ty, e
)
});
fulfill.register_bound(&infcx,
full_env,
ty,
trait_did,
ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID));
fulfill
.select_all_or_error(&infcx)
.unwrap_or_else(|e| {
panic!("Unable to fulfill trait {:?} for '{:?}': {:?}",
trait_did,
ty,
e)
});
let names_map: FxHashMap<String, String> = generics
.regions
@ -227,7 +231,12 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let vid_to_region = self.map_vid_to_region(&region_data);
let info = AutoTraitInfo { full_user_env, region_data, names_map, vid_to_region };
let info = AutoTraitInfo {
full_user_env,
region_data,
names_map,
vid_to_region,
};
return AutoTraitResult::PositiveImpl(auto_trait_callback(&infcx, info));
});
@ -272,17 +281,16 @@ 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>(
&self,
infcx: & InferCtxt<'b, 'tcx, 'c>,
ty_did: DefId,
trait_did: DefId,
ty: ty::Ty<'c>,
param_env: ty::ParamEnv<'c>,
user_env: ty::ParamEnv<'c>,
fresh_preds: &mut FxHashSet<ty::Predicate<'c>>,
only_projections: bool,
) -> Option<(ty::ParamEnv<'c>, ty::ParamEnv<'c>)> {
pub fn evaluate_predicates<'b, 'gcx, 'c>(&self,
infcx: &InferCtxt<'b, 'tcx, 'c>,
ty_did: DefId,
trait_did: DefId,
ty: ty::Ty<'c>,
param_env: ty::ParamEnv<'c>,
user_env: ty::ParamEnv<'c>,
fresh_preds: &mut FxHashSet<ty::Predicate<'c>>,
only_projections: bool)
-> Option<(ty::ParamEnv<'c>, ty::ParamEnv<'c>)> {
let tcx = infcx.tcx;
let mut select = SelectionContext::new(&infcx);
@ -290,11 +298,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
let mut already_visited = FxHashSet();
let mut predicates = VecDeque::new();
predicates.push_back(ty::Binder::bind(ty::TraitPredicate {
trait_ref: ty::TraitRef {
def_id: trait_did,
substs: infcx.tcx.mk_substs_trait(ty, &[]),
},
}));
trait_ref: ty::TraitRef {
def_id: trait_did,
substs: infcx.tcx.mk_substs_trait(ty, &[]),
},
}));
let mut computed_preds: FxHashSet<_> = param_env.caller_bounds.iter().cloned().collect();
let mut user_computed_preds: FxHashSet<_> =
@ -316,15 +324,13 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
&Ok(Some(ref vtable)) => {
let obligations = vtable.clone().nested_obligations().into_iter();
if !self.evaluate_nested_obligations(
ty,
obligations,
&mut user_computed_preds,
fresh_preds,
&mut predicates,
&mut select,
only_projections,
) {
if !self.evaluate_nested_obligations(ty,
obligations,
&mut user_computed_preds,
fresh_preds,
&mut predicates,
&mut select,
only_projections) {
return None;
}
}
@ -335,13 +341,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
user_computed_preds.insert(ty::Predicate::Trait(pred.clone()));
predicates.push_back(pred);
} else {
debug!(
"evaluate_nested_obligations: Unimplemented found, bailing: {:?} {:?} \
{:?}",
ty,
pred,
pred.skip_binder().trait_ref.substs
);
debug!("evaluate_nested_obligations: Unimplemented found, bailing: \
{:?} {:?} {:?}",
ty,
pred,
pred.skip_binder().trait_ref.substs);
return None;
}
}
@ -351,21 +355,17 @@ 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());
new_env = ty::ParamEnv::new(
tcx.mk_predicates(normalized_preds),
param_env.reveal,
);
new_env = ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal);
}
let final_user_env = ty::ParamEnv::new(
tcx.mk_predicates(user_computed_preds.into_iter()),
user_env.reveal,
);
debug!(
"evaluate_nested_obligations(ty_did={:?}, trait_did={:?}): succeeded with '{:?}' \
let final_user_env = ty::ParamEnv::new(tcx.mk_predicates(user_computed_preds.into_iter()),
user_env.reveal);
debug!("evaluate_nested_obligations(ty_did={:?}, trait_did={:?}): succeeded with '{:?}' \
'{:?}'",
ty_did, trait_did, new_env, final_user_env
);
ty_did,
trait_did,
new_env,
final_user_env);
return Some((new_env, final_user_env));
}
@ -380,9 +380,13 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
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)
})
names_map
.get(&name)
.unwrap_or_else(|| {
panic!("Missing lifetime with name {:?} for {:?}",
name,
region)
})
})
.unwrap_or(&"'static".to_string())
.clone()
@ -390,10 +394,9 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// 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>(
&self,
regions: &RegionConstraintData<'cx>,
) -> FxHashMap<ty::RegionVid, ty::Region<'cx>> {
pub fn map_vid_to_region<'cx>(&self,
regions: &RegionConstraintData<'cx>)
-> FxHashMap<ty::RegionVid, ty::Region<'cx>> {
let mut vid_map: FxHashMap<RegionTarget<'cx>, RegionDeps<'cx>> = FxHashMap();
let mut finished_map = FxHashMap();
@ -496,31 +499,34 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
return match substs.type_at(0).sty {
ty::TyParam(_) => true,
ty::TyProjection(p) => self.is_of_param(p.substs),
_ => false,
};
ty::TyParam(_) => true,
ty::TyProjection(p) => self.is_of_param(p.substs),
_ => false,
};
}
pub fn evaluate_nested_obligations<'b, 'c, 'd, 'cx,
T: Iterator<Item = Obligation<'cx, ty::Predicate<'cx>>>>(
&self,
ty: ty::Ty,
nested: T,
computed_preds: &'b mut FxHashSet<ty::Predicate<'cx>>,
fresh_preds: &'b mut FxHashSet<ty::Predicate<'cx>>,
predicates: &'b mut VecDeque<ty::PolyTraitPredicate<'cx>>,
select: &mut SelectionContext<'c, 'd, 'cx>,
only_projections: bool,
) -> bool {
pub fn evaluate_nested_obligations<'b,
'c,
'd,
'cx,
T: Iterator<Item = Obligation<'cx, ty::Predicate<'cx>>>>
(&self,
ty: ty::Ty,
nested: T,
computed_preds: &'b mut FxHashSet<ty::Predicate<'cx>>,
fresh_preds: &'b mut FxHashSet<ty::Predicate<'cx>>,
predicates: &'b mut VecDeque<ty::PolyTraitPredicate<'cx>>,
select: &mut SelectionContext<'c, 'd, 'cx>,
only_projections: bool)
-> bool {
let dummy_cause = ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID);
for (obligation, predicate) in nested
.filter(|o| o.recursion_depth == 1)
.map(|o| (o.clone(), o.predicate.clone()))
{
let is_new_pred =
fresh_preds.insert(self.clean_pred(select.infcx(), predicate.clone()));
for (obligation, predicate) in
nested
.filter(|o| o.recursion_depth == 1)
.map(|o| (o.clone(), o.predicate.clone())) {
let is_new_pred = fresh_preds
.insert(self.clean_pred(select.infcx(), predicate.clone()));
match &predicate {
&ty::Predicate::Trait(ref p) => {
@ -537,28 +543,22 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
if self.is_of_param(p.skip_binder().projection_ty.substs) && is_new_pred {
computed_preds.insert(predicate);
} else {
match poly_project_and_unify_type(
select,
&obligation.with(p.clone()),
) {
match poly_project_and_unify_type(select, &obligation.with(p.clone())) {
Err(e) => {
debug!(
"evaluate_nested_obligations: Unable to unify predicate \
debug!("evaluate_nested_obligations: Unable to unify predicate \
'{:?}' '{:?}', bailing out",
ty, e
);
ty,
e);
return false;
}
Ok(Some(v)) => {
if !self.evaluate_nested_obligations(
ty,
v.clone().iter().cloned(),
computed_preds,
fresh_preds,
predicates,
select,
only_projections,
) {
if !self.evaluate_nested_obligations(ty,
v.clone().iter().cloned(),
computed_preds,
fresh_preds,
predicates,
select,
only_projections) {
return false;
}
}
@ -570,36 +570,37 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
&ty::Predicate::RegionOutlives(ref binder) => {
if let Err(_) = select
.infcx()
.region_outlives_predicate(&dummy_cause, binder)
{
.infcx()
.region_outlives_predicate(&dummy_cause, binder) {
return false;
}
}
&ty::Predicate::TypeOutlives(ref binder) => {
match (
binder.no_late_bound_regions(),
binder.map_bound_ref(|pred| pred.0).no_late_bound_regions(),
) {
match (binder.no_late_bound_regions(),
binder.map_bound_ref(|pred| pred.0).no_late_bound_regions()) {
(None, Some(t_a)) => {
select.infcx().register_region_obligation(
ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: select.infcx().tcx.types.re_static,
cause: dummy_cause.clone(),
},
);
select
.infcx()
.register_region_obligation(ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: select
.infcx()
.tcx
.types
.re_static,
cause: dummy_cause.clone(),
});
}
(Some(ty::OutlivesPredicate(t_a, r_b)), _) => {
select.infcx().register_region_obligation(
ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: dummy_cause.clone(),
},
);
select
.infcx()
.register_region_obligation(ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: dummy_cause.clone(),
});
}
_ => {}
};
@ -610,11 +611,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
return true;
}
pub fn clean_pred<'c, 'd, 'cx>(
&self,
infcx: &InferCtxt<'c, 'd, 'cx>,
p: ty::Predicate<'cx>,
) -> ty::Predicate<'cx> {
pub fn clean_pred<'c, 'd, 'cx>(&self,
infcx: &InferCtxt<'c, 'd, 'cx>,
p: ty::Predicate<'cx>)
-> ty::Predicate<'cx> {
infcx.freshen(p)
}
}
@ -632,8 +632,9 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionReplacer<'a, 'gcx, 'tcx> {
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
(match r {
&ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(),
_ => None,
}).unwrap_or_else(|| r.super_fold_with(self))
&ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(),
_ => None,
})
.unwrap_or_else(|| r.super_fold_with(self))
}
}