Skip to content

Commit

Permalink
evaluate projections outside the outer probe in recursive evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
Ariel Ben-Yehuda committed Nov 15, 2015
1 parent f37b4fe commit 9c6d35d
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 156 deletions.
202 changes: 46 additions & 156 deletions src/librustc/middle/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// except according to those terms.

//! See `README.md` for high-level documentation
#![allow(dead_code)] // FIXME -- just temporarily
pub use self::MethodMatchResult::*;
pub use self::MethodMatchedData::*;
Expand Down Expand Up @@ -190,7 +189,6 @@ pub enum MethodMatchedData {
/// parameter environment.
#[derive(PartialEq,Eq,Debug,Clone)]
enum SelectionCandidate<'tcx> {
PhantomFnCandidate,
BuiltinCandidate(ty::BuiltinBound),
ParamCandidate(ty::PolyTraitRef<'tcx>),
ImplCandidate(DefId),
Expand Down Expand Up @@ -403,8 +401,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!("evaluate_obligation({:?})",
obligation);

self.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
.may_apply()
self.infcx.probe(|_| {
self.evaluate_predicate_recursively(TraitObligationStackList::empty(), obligation)
.may_apply()
})
}

fn evaluate_predicates_recursively<'a,'o,I>(&mut self,
Expand All @@ -415,7 +415,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
{
let mut result = EvaluatedToOk;
for obligation in predicates {
match self.evaluate_predicate_recursively(stack, obligation) {
let eval = self.evaluate_predicate_recursively(stack, obligation);
debug!("evaluate_predicate_recursively({:?}) = {:?}",
obligation, eval);
match eval {
EvaluatedToErr => { return EvaluatedToErr; }
EvaluatedToAmbig => { result = EvaluatedToAmbig; }
EvaluatedToUnknown => {
Expand Down Expand Up @@ -454,10 +457,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

ty::Predicate::Equate(ref p) => {
let result = self.infcx.probe(|_| {
self.infcx.equality_predicate(obligation.cause.span, p)
});
match result {
// does this code ever run?
match self.infcx.equality_predicate(obligation.cause.span, p) {
Ok(()) => EvaluatedToOk,
Err(_) => EvaluatedToErr
}
Expand Down Expand Up @@ -489,21 +490,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

ty::Predicate::Projection(ref data) => {
self.infcx.probe(|_| {
let project_obligation = obligation.with(data.clone());
match project::poly_project_and_unify_type(self, &project_obligation) {
Ok(Some(subobligations)) => {
self.evaluate_predicates_recursively(previous_stack,
subobligations.iter())
}
Ok(None) => {
EvaluatedToAmbig
}
Err(_) => {
EvaluatedToErr
}
let project_obligation = obligation.with(data.clone());
match project::poly_project_and_unify_type(self, &project_obligation) {
Ok(Some(subobligations)) => {
self.evaluate_predicates_recursively(previous_stack,
subobligations.iter())
}
})
Ok(None) => {
EvaluatedToAmbig
}
Err(_) => {
EvaluatedToErr
}
}
}
}
}
Expand Down Expand Up @@ -610,40 +609,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

match self.candidate_from_obligation(stack) {
Ok(Some(c)) => self.winnow_candidate(stack, &c),
Ok(Some(c)) => self.evaluate_candidate(stack, &c),
Ok(None) => EvaluatedToAmbig,
Err(..) => EvaluatedToErr
}
}

/// Evaluates whether the impl with id `impl_def_id` could be applied to the self type
/// `obligation_self_ty`. This can be used either for trait or inherent impls.
pub fn evaluate_impl(&mut self,
impl_def_id: DefId,
obligation: &TraitObligation<'tcx>)
-> bool
/// Further evaluate `candidate` to decide whether all type parameters match and whether nested
/// obligations are met. Returns true if `candidate` remains viable after this further
/// scrutiny.
fn evaluate_candidate<'o>(&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
candidate: &SelectionCandidate<'tcx>)
-> EvaluationResult
{
debug!("evaluate_impl(impl_def_id={:?}, obligation={:?})",
impl_def_id,
obligation);

self.infcx.probe(|snapshot| {
match self.match_impl(impl_def_id, obligation, snapshot) {
Ok((substs, skol_map)) => {
let vtable_impl = self.vtable_impl(impl_def_id,
substs,
obligation.cause.clone(),
obligation.recursion_depth + 1,
skol_map,
snapshot);
self.winnow_selection(TraitObligationStackList::empty(),
VtableImpl(vtable_impl)).may_apply()
}
Err(()) => {
false
debug!("evaluate_candidate: depth={} candidate={:?}",
stack.obligation.recursion_depth, candidate);
let result = self.infcx.probe(|_| {
let candidate = (*candidate).clone();
match self.confirm_candidate(stack.obligation, candidate) {
Ok(selection) => {
self.evaluate_predicates_recursively(
stack.list(),
selection.nested_obligations().iter())
}
Err(..) => EvaluatedToErr
}
})
});
debug!("evaluate_candidate: depth={} result={:?}",
stack.obligation.recursion_depth, result);
result
}

fn pick_evaluation_cache(&self) -> &EvaluationCache<'tcx> {
Expand Down Expand Up @@ -779,7 +774,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Instead, we select the right impl now but report `Bar does
// not implement Clone`.
if candidates.len() > 1 {
candidates.retain(|c| self.winnow_candidate(stack, c).may_apply())
candidates.retain(|c| self.evaluate_candidate(stack, c).may_apply())
}

// If there are STILL multiple candidate, we can further reduce
Expand Down Expand Up @@ -1184,9 +1179,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(obligations) => {
self.evaluate_predicates_recursively(stack.list(), obligations.iter())
}
Err(()) => {
EvaluatedToErr
}
Err(()) => EvaluatedToErr
}
})
}
Expand Down Expand Up @@ -1525,37 +1518,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// attempt to evaluate recursive bounds to see if they are
// satisfied.

/// Further evaluate `candidate` to decide whether all type parameters match and whether nested
/// obligations are met. Returns true if `candidate` remains viable after this further
/// scrutiny.
fn winnow_candidate<'o>(&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
candidate: &SelectionCandidate<'tcx>)
-> EvaluationResult
{
debug!("winnow_candidate: candidate={:?}", candidate);
let result = self.infcx.probe(|_| {
let candidate = (*candidate).clone();
match self.confirm_candidate(stack.obligation, candidate) {
Ok(selection) => self.winnow_selection(stack.list(),
selection),
Err(..) => EvaluatedToErr
}
});
debug!("winnow_candidate depth={} result={:?}",
stack.obligation.recursion_depth, result);
result
}

fn winnow_selection<'o>(&mut self,
stack: TraitObligationStackList<'o,'tcx>,
selection: Selection<'tcx>)
-> EvaluationResult
{
self.evaluate_predicates_recursively(stack,
selection.nested_obligations().iter())
}

/// Returns true if `candidate_i` should be dropped in favor of
/// `candidate_j`. Generally speaking we will drop duplicate
/// candidates and prefer where-clause candidates.
Expand All @@ -1581,9 +1543,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
"default implementations shouldn't be recorded \
when there are other valid candidates");
}
&PhantomFnCandidate => {
self.tcx().sess.bug("PhantomFn didn't short-circuit selection");
}
&ImplCandidate(..) |
&ClosureCandidate(..) |
&FnPointerCandidate(..) |
Expand Down Expand Up @@ -2013,7 +1972,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
try!(self.confirm_builtin_candidate(obligation, builtin_bound))))
}

PhantomFnCandidate |
ErrorCandidate => {
Ok(VtableBuiltin(VtableBuiltinData { nested: vec![] }))
}
Expand Down Expand Up @@ -2788,74 +2746,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}

/// Determines whether the self type declared against
/// `impl_def_id` matches `obligation_self_ty`. If successful,
/// returns the substitutions used to make them match. See
/// `match_impl()`. For example, if `impl_def_id` is declared
/// as:
///
/// impl<T:Copy> Foo for Box<T> { ... }
///
/// and `obligation_self_ty` is `int`, we'd get back an `Err(_)`
/// result. But if `obligation_self_ty` were `Box<int>`, we'd get
/// back `Ok(T=int)`.
fn match_inherent_impl(&mut self,
impl_def_id: DefId,
obligation_cause: &ObligationCause,
obligation_self_ty: Ty<'tcx>)
-> Result<Substs<'tcx>,()>
{
// Create fresh type variables for each type parameter declared
// on the impl etc.
let impl_substs = util::fresh_type_vars_for_impl(self.infcx,
obligation_cause.span,
impl_def_id);

// Find the self type for the impl.
let impl_self_ty = self.tcx().lookup_item_type(impl_def_id).ty;
let impl_self_ty = impl_self_ty.subst(self.tcx(), &impl_substs);

debug!("match_impl_self_types(obligation_self_ty={:?}, impl_self_ty={:?})",
obligation_self_ty,
impl_self_ty);

match self.match_self_types(obligation_cause,
impl_self_ty,
obligation_self_ty) {
Ok(()) => {
debug!("Matched impl_substs={:?}", impl_substs);
Ok(impl_substs)
}
Err(()) => {
debug!("NoMatch");
Err(())
}
}
}

fn match_self_types(&mut self,
cause: &ObligationCause,

// The self type provided by the impl/caller-obligation:
provided_self_ty: Ty<'tcx>,

// The self type the obligation is for:
required_self_ty: Ty<'tcx>)
-> Result<(),()>
{
// FIXME(#5781) -- equating the types is stronger than
// necessary. Should consider variance of trait w/r/t Self.

let origin = infer::RelateSelfType(cause.span);
match self.infcx.eq_types(false,
origin,
provided_self_ty,
required_self_ty) {
Ok(()) => Ok(()),
Err(_) => Err(()),
}
}

///////////////////////////////////////////////////////////////////////////
// Miscellany

Expand Down
31 changes: 31 additions & 0 deletions src/test/run-pass/coherence-rfc447-constrained.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2015 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.

// check that trait matching can handle impls whose types are only
// constrained by a projection.

trait IsU32 {}
impl IsU32 for u32 {}

trait Mirror { type Image: ?Sized; }
impl<T: ?Sized> Mirror for T { type Image = T; }

trait Bar {}
impl<U: Mirror, V: Mirror<Image=L>, L: Mirror<Image=U>> Bar for V
where U::Image: IsU32 {}

trait Foo { fn name() -> &'static str; }
impl Foo for u64 { fn name() -> &'static str { "u64" } }
impl<T: Bar> Foo for T { fn name() -> &'static str { "Bar" }}

fn main() {
assert_eq!(<u64 as Foo>::name(), "u64");
assert_eq!(<u32 as Foo>::name(), "Bar");
}

0 comments on commit 9c6d35d

Please sign in to comment.