evaluate projections outside the outer probe in recursive evaluation
This commit is contained in:
parent
f37b4fe84f
commit
9c6d35d037
2 changed files with 77 additions and 156 deletions
|
@ -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::*;
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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 => {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
|
@ -1184,9 +1179,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
Ok(obligations) => {
|
||||
self.evaluate_predicates_recursively(stack.list(), obligations.iter())
|
||||
}
|
||||
Err(()) => {
|
||||
EvaluatedToErr
|
||||
}
|
||||
Err(()) => EvaluatedToErr
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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(..) |
|
||||
|
@ -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![] }))
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
|
31
src/test/run-pass/coherence-rfc447-constrained.rs
Normal file
31
src/test/run-pass/coherence-rfc447-constrained.rs
Normal file
|
@ -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");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue