Inline and remove process_predicate.

Because it has a single callsite.

This patch is large but trivial, doing the minimal amount of work
(mostly `self.` insertions) necessary.
This commit is contained in:
Nicholas Nethercote 2018-06-07 09:30:43 +10:00
parent cdfd9ca088
commit b18a22a384

View File

@ -262,11 +262,261 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
type Obligation = PendingPredicateObligation<'tcx>;
type Error = FulfillmentErrorCode<'tcx>;
/// Processes a predicate obligation and returns either:
/// - `Ok(Some(v))` if the predicate is true, presuming that `v` are also true
/// - `Ok(None)` if we don't have enough info to be sure
/// - `Err` if the predicate does not hold
fn process_obligation(&mut self,
obligation: &mut Self::Obligation)
pending_obligation: &mut Self::Obligation)
-> Result<Option<Vec<Self::Obligation>>, Self::Error>
{
process_predicate(self.selcx, obligation, self.register_region_obligations)
// if we were stalled on some unresolved variables, first check
// whether any of them have been resolved; if not, don't bother
// doing more work yet
if !pending_obligation.stalled_on.is_empty() {
if pending_obligation.stalled_on.iter().all(|&ty| {
let resolved_ty = self.selcx.infcx().shallow_resolve(&ty);
resolved_ty == ty // nothing changed here
}) {
debug!("process_predicate: pending obligation {:?} still stalled on {:?}",
self.selcx.infcx()
.resolve_type_vars_if_possible(&pending_obligation.obligation),
pending_obligation.stalled_on);
return Ok(None);
}
pending_obligation.stalled_on = vec![];
}
let obligation = &mut pending_obligation.obligation;
if obligation.predicate.has_infer_types() {
obligation.predicate =
self.selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate);
}
match obligation.predicate {
ty::Predicate::Trait(ref data) => {
let trait_obligation = obligation.with(data.clone());
if data.is_global() && !data.has_late_bound_regions() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
if self.selcx.infcx().predicate_must_hold(&obligation) {
debug!("selecting trait `{:?}` at depth {} evaluated to holds",
data, obligation.recursion_depth);
return Ok(Some(vec![]))
}
}
match self.selcx.select(&trait_obligation) {
Ok(Some(vtable)) => {
debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
data, obligation.recursion_depth);
Ok(Some(mk_pending(vtable.nested_obligations())))
}
Ok(None) => {
debug!("selecting trait `{:?}` at depth {} yielded Ok(None)",
data, obligation.recursion_depth);
// This is a bit subtle: for the most part, the
// only reason we can fail to make progress on
// trait selection is because we don't have enough
// information about the types in the trait. One
// exception is that we sometimes haven't decided
// what kind of closure a closure is. *But*, in
// that case, it turns out, the type of the
// closure will also change, because the closure
// also includes references to its upvars as part
// of its type, and those types are resolved at
// the same time.
//
// FIXME(#32286) logic seems false if no upvars
pending_obligation.stalled_on =
trait_ref_type_vars(self.selcx, data.to_poly_trait_ref());
debug!("process_predicate: pending obligation {:?} now stalled on {:?}",
self.selcx.infcx().resolve_type_vars_if_possible(obligation),
pending_obligation.stalled_on);
Ok(None)
}
Err(selection_err) => {
info!("selecting trait `{:?}` at depth {} yielded Err",
data, obligation.recursion_depth);
Err(CodeSelectionError(selection_err))
}
}
}
ty::Predicate::RegionOutlives(ref binder) => {
match self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder) {
Ok(()) => Ok(Some(Vec::new())),
Err(_) => Err(CodeSelectionError(Unimplemented)),
}
}
ty::Predicate::TypeOutlives(ref binder) => {
// Check if there are higher-ranked regions.
match binder.no_late_bound_regions() {
// If there are, inspect the underlying type further.
None => {
// Convert from `Binder<OutlivesPredicate<Ty, Region>>` to `Binder<Ty>`.
let binder = binder.map_bound_ref(|pred| pred.0);
// Check if the type has any bound regions.
match binder.no_late_bound_regions() {
// If so, this obligation is an error (for now). Eventually we should be
// able to support additional cases here, like `for<'a> &'a str: 'a`.
None => {
Err(CodeSelectionError(Unimplemented))
}
// Otherwise, we have something of the form
// `for<'a> T: 'a where 'a not in T`, which we can treat as
// `T: 'static`.
Some(t_a) => {
let r_static = self.selcx.tcx().types.re_static;
if self.register_region_obligations {
self.selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_static,
cause: obligation.cause.clone(),
});
}
Ok(Some(vec![]))
}
}
}
// If there aren't, register the obligation.
Some(ty::OutlivesPredicate(t_a, r_b)) => {
if self.register_region_obligations {
self.selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: obligation.cause.clone()
});
}
Ok(Some(vec![]))
}
}
}
ty::Predicate::Projection(ref data) => {
let project_obligation = obligation.with(data.clone());
match project::poly_project_and_unify_type(self.selcx, &project_obligation) {
Ok(None) => {
let tcx = self.selcx.tcx();
pending_obligation.stalled_on =
trait_ref_type_vars(self.selcx, data.to_poly_trait_ref(tcx));
Ok(None)
}
Ok(Some(os)) => Ok(Some(mk_pending(os))),
Err(e) => Err(CodeProjectionError(e))
}
}
ty::Predicate::ObjectSafe(trait_def_id) => {
if !self.selcx.tcx().is_object_safe(trait_def_id) {
Err(CodeSelectionError(Unimplemented))
} else {
Ok(Some(Vec::new()))
}
}
ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
match self.selcx.infcx().closure_kind(closure_def_id, closure_substs) {
Some(closure_kind) => {
if closure_kind.extends(kind) {
Ok(Some(vec![]))
} else {
Err(CodeSelectionError(Unimplemented))
}
}
None => {
Ok(None)
}
}
}
ty::Predicate::WellFormed(ty) => {
match ty::wf::obligations(self.selcx.infcx(),
obligation.param_env,
obligation.cause.body_id,
ty, obligation.cause.span) {
None => {
pending_obligation.stalled_on = vec![ty];
Ok(None)
}
Some(os) => Ok(Some(mk_pending(os)))
}
}
ty::Predicate::Subtype(ref subtype) => {
match self.selcx.infcx().subtype_predicate(&obligation.cause,
obligation.param_env,
subtype) {
None => {
// None means that both are unresolved.
pending_obligation.stalled_on = vec![subtype.skip_binder().a,
subtype.skip_binder().b];
Ok(None)
}
Some(Ok(ok)) => {
Ok(Some(mk_pending(ok.obligations)))
}
Some(Err(err)) => {
let expected_found = ExpectedFound::new(subtype.skip_binder().a_is_expected,
subtype.skip_binder().a,
subtype.skip_binder().b);
Err(FulfillmentErrorCode::CodeSubtypeError(expected_found, err))
}
}
}
ty::Predicate::ConstEvaluatable(def_id, substs) => {
match self.selcx.tcx().lift_to_global(&obligation.param_env) {
None => {
Ok(None)
}
Some(param_env) => {
match self.selcx.tcx().lift_to_global(&substs) {
Some(substs) => {
let instance = ty::Instance::resolve(
self.selcx.tcx().global_tcx(),
param_env,
def_id,
substs,
);
if let Some(instance) = instance {
let cid = GlobalId {
instance,
promoted: None,
};
match self.selcx.tcx().at(obligation.cause.span)
.const_eval(param_env.and(cid)) {
Ok(_) => Ok(Some(vec![])),
Err(err) => Err(CodeSelectionError(ConstEvalFailure(err)))
}
} else {
Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr {
span: obligation.cause.span,
kind: ErrKind::CouldNotResolve.into(),
})))
}
},
None => {
pending_obligation.stalled_on = substs.types().collect();
Ok(None)
}
}
}
}
}
}
}
fn process_backedge<'c, I>(&mut self, cycle: I,
@ -295,263 +545,6 @@ fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 't
.collect()
}
/// Processes a predicate obligation and returns either:
/// - `Ok(Some(v))` if the predicate is true, presuming that `v` are also true
/// - `Ok(None)` if we don't have enough info to be sure
/// - `Err` if the predicate does not hold
fn process_predicate<'a, 'gcx, 'tcx>(
selcx: &mut SelectionContext<'a, 'gcx, 'tcx>,
pending_obligation: &mut PendingPredicateObligation<'tcx>,
register_region_obligations: bool)
-> Result<Option<Vec<PendingPredicateObligation<'tcx>>>,
FulfillmentErrorCode<'tcx>>
{
// if we were stalled on some unresolved variables, first check
// whether any of them have been resolved; if not, don't bother
// doing more work yet
if !pending_obligation.stalled_on.is_empty() {
if pending_obligation.stalled_on.iter().all(|&ty| {
let resolved_ty = selcx.infcx().shallow_resolve(&ty);
resolved_ty == ty // nothing changed here
}) {
debug!("process_predicate: pending obligation {:?} still stalled on {:?}",
selcx.infcx().resolve_type_vars_if_possible(&pending_obligation.obligation),
pending_obligation.stalled_on);
return Ok(None);
}
pending_obligation.stalled_on = vec![];
}
let obligation = &mut pending_obligation.obligation;
if obligation.predicate.has_infer_types() {
obligation.predicate = selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate);
}
match obligation.predicate {
ty::Predicate::Trait(ref data) => {
let trait_obligation = obligation.with(data.clone());
if data.is_global() && !data.has_late_bound_regions() {
// no type variables present, can use evaluation for better caching.
// FIXME: consider caching errors too.
if selcx.infcx().predicate_must_hold(&obligation) {
debug!("selecting trait `{:?}` at depth {} evaluated to holds",
data, obligation.recursion_depth);
return Ok(Some(vec![]))
}
}
match selcx.select(&trait_obligation) {
Ok(Some(vtable)) => {
debug!("selecting trait `{:?}` at depth {} yielded Ok(Some)",
data, obligation.recursion_depth);
Ok(Some(mk_pending(vtable.nested_obligations())))
}
Ok(None) => {
debug!("selecting trait `{:?}` at depth {} yielded Ok(None)",
data, obligation.recursion_depth);
// This is a bit subtle: for the most part, the
// only reason we can fail to make progress on
// trait selection is because we don't have enough
// information about the types in the trait. One
// exception is that we sometimes haven't decided
// what kind of closure a closure is. *But*, in
// that case, it turns out, the type of the
// closure will also change, because the closure
// also includes references to its upvars as part
// of its type, and those types are resolved at
// the same time.
//
// FIXME(#32286) logic seems false if no upvars
pending_obligation.stalled_on =
trait_ref_type_vars(selcx, data.to_poly_trait_ref());
debug!("process_predicate: pending obligation {:?} now stalled on {:?}",
selcx.infcx().resolve_type_vars_if_possible(obligation),
pending_obligation.stalled_on);
Ok(None)
}
Err(selection_err) => {
info!("selecting trait `{:?}` at depth {} yielded Err",
data, obligation.recursion_depth);
Err(CodeSelectionError(selection_err))
}
}
}
ty::Predicate::RegionOutlives(ref binder) => {
match selcx.infcx().region_outlives_predicate(&obligation.cause, binder) {
Ok(()) => Ok(Some(Vec::new())),
Err(_) => Err(CodeSelectionError(Unimplemented)),
}
}
ty::Predicate::TypeOutlives(ref binder) => {
// Check if there are higher-ranked regions.
match binder.no_late_bound_regions() {
// If there are, inspect the underlying type further.
None => {
// Convert from `Binder<OutlivesPredicate<Ty, Region>>` to `Binder<Ty>`.
let binder = binder.map_bound_ref(|pred| pred.0);
// Check if the type has any bound regions.
match binder.no_late_bound_regions() {
// If so, this obligation is an error (for now). Eventually we should be
// able to support additional cases here, like `for<'a> &'a str: 'a`.
None => {
Err(CodeSelectionError(Unimplemented))
}
// Otherwise, we have something of the form
// `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`.
Some(t_a) => {
let r_static = selcx.tcx().types.re_static;
if register_region_obligations {
selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_static,
cause: obligation.cause.clone(),
});
}
Ok(Some(vec![]))
}
}
}
// If there aren't, register the obligation.
Some(ty::OutlivesPredicate(t_a, r_b)) => {
if register_region_obligations {
selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: obligation.cause.clone()
});
}
Ok(Some(vec![]))
}
}
}
ty::Predicate::Projection(ref data) => {
let project_obligation = obligation.with(data.clone());
match project::poly_project_and_unify_type(selcx, &project_obligation) {
Ok(None) => {
let tcx = selcx.tcx();
pending_obligation.stalled_on =
trait_ref_type_vars(selcx, data.to_poly_trait_ref(tcx));
Ok(None)
}
Ok(Some(os)) => Ok(Some(mk_pending(os))),
Err(e) => Err(CodeProjectionError(e))
}
}
ty::Predicate::ObjectSafe(trait_def_id) => {
if !selcx.tcx().is_object_safe(trait_def_id) {
Err(CodeSelectionError(Unimplemented))
} else {
Ok(Some(Vec::new()))
}
}
ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => {
match selcx.infcx().closure_kind(closure_def_id, closure_substs) {
Some(closure_kind) => {
if closure_kind.extends(kind) {
Ok(Some(vec![]))
} else {
Err(CodeSelectionError(Unimplemented))
}
}
None => {
Ok(None)
}
}
}
ty::Predicate::WellFormed(ty) => {
match ty::wf::obligations(selcx.infcx(),
obligation.param_env,
obligation.cause.body_id,
ty, obligation.cause.span) {
None => {
pending_obligation.stalled_on = vec![ty];
Ok(None)
}
Some(os) => Ok(Some(mk_pending(os)))
}
}
ty::Predicate::Subtype(ref subtype) => {
match selcx.infcx().subtype_predicate(&obligation.cause,
obligation.param_env,
subtype) {
None => {
// none means that both are unresolved
pending_obligation.stalled_on = vec![subtype.skip_binder().a,
subtype.skip_binder().b];
Ok(None)
}
Some(Ok(ok)) => {
Ok(Some(mk_pending(ok.obligations)))
}
Some(Err(err)) => {
let expected_found = ExpectedFound::new(subtype.skip_binder().a_is_expected,
subtype.skip_binder().a,
subtype.skip_binder().b);
Err(FulfillmentErrorCode::CodeSubtypeError(expected_found, err))
}
}
}
ty::Predicate::ConstEvaluatable(def_id, substs) => {
match selcx.tcx().lift_to_global(&obligation.param_env) {
None => {
Ok(None)
}
Some(param_env) => {
match selcx.tcx().lift_to_global(&substs) {
Some(substs) => {
let instance = ty::Instance::resolve(
selcx.tcx().global_tcx(),
param_env,
def_id,
substs,
);
if let Some(instance) = instance {
let cid = GlobalId {
instance,
promoted: None,
};
match selcx.tcx().at(obligation.cause.span)
.const_eval(param_env.and(cid)) {
Ok(_) => Ok(Some(vec![])),
Err(err) => Err(CodeSelectionError(ConstEvalFailure(err)))
}
} else {
Err(CodeSelectionError(ConstEvalFailure(ConstEvalErr {
span: obligation.cause.span,
kind: ErrKind::CouldNotResolve.into(),
})))
}
},
None => {
pending_obligation.stalled_on = substs.types().collect();
Ok(None)
}
}
}
}
}
}
}
fn to_fulfillment_error<'tcx>(
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>)
-> FulfillmentError<'tcx>