From ea1ad792f9c182f18d865d5ff34a86a8ef0e4421 Mon Sep 17 00:00:00 2001 From: Niko Matsakis <niko@alum.mit.edu> Date: Fri, 2 Jan 2015 11:39:47 -0500 Subject: [PATCH] Evaluate projection predicates during trait selection. Fixes #20296. --- src/librustc/middle/traits/select.rs | 46 +++++++++--- .../associated-types-conditional-dispatch.rs | 73 +++++++++++++++++++ 2 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 src/test/run-pass/associated-types-conditional-dispatch.rs diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index f9dced088f8..ef8fbdfaa6c 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -289,6 +289,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn evaluate_predicates_recursively<'a,'o,I>(&mut self, + stack: Option<&TraitObligationStack<'o, 'tcx>>, + mut predicates: I) + -> EvaluationResult<'tcx> + where I : Iterator<&'a PredicateObligation<'tcx>>, 'tcx:'a + { + let mut result = EvaluatedToOk; + for obligation in predicates { + match self.evaluate_predicate_recursively(stack, obligation) { + EvaluatedToErr(e) => { return EvaluatedToErr(e); } + EvaluatedToAmbig => { result = EvaluatedToAmbig; } + EvaluatedToOk => { } + } + } + result + } + fn evaluate_predicate_recursively<'o>(&mut self, previous_stack: Option<&TraitObligationStack<'o, 'tcx>>, obligation: &PredicateObligation<'tcx>) @@ -320,9 +337,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { EvaluatedToOk } - ty::Predicate::Projection(..) => { - // FIXME(#20296) -- we should be able to give a more precise answer here - EvaluatedToAmbig + ty::Predicate::Projection(ref data) => { + let result = self.infcx.probe(|_| { + let project_obligation = obligation.with(data.clone()); + project::poly_project_and_unify_type(self, &project_obligation) + }); + match result { + Ok(Some(subobligations)) => { + self.evaluate_predicates_recursively(previous_stack, subobligations.iter()) + } + Ok(None) => { + EvaluatedToAmbig + } + Err(_) => { + EvaluatedToErr(Unimplemented) + } + } } } } @@ -1026,15 +1056,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { selection: Selection<'tcx>) -> EvaluationResult<'tcx> { - let mut result = EvaluatedToOk; - for obligation in selection.iter_nested() { - match self.evaluate_predicate_recursively(stack, obligation) { - EvaluatedToErr(e) => { return EvaluatedToErr(e); } - EvaluatedToAmbig => { result = EvaluatedToAmbig; } - EvaluatedToOk => { } - } - } - result + self.evaluate_predicates_recursively(stack, selection.iter_nested()) } /// Returns true if `candidate_i` should be dropped in favor of `candidate_j`. diff --git a/src/test/run-pass/associated-types-conditional-dispatch.rs b/src/test/run-pass/associated-types-conditional-dispatch.rs new file mode 100644 index 00000000000..4efd0ef348d --- /dev/null +++ b/src/test/run-pass/associated-types-conditional-dispatch.rs @@ -0,0 +1,73 @@ +// Copyright 2014 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. + +// Test that we evaluate projection predicates to winnow out +// candidates during trait selection and method resolution (#20296). +// If we don't properly winnow out candidates based on the output type +// `Output=[A]`, then the impl marked with `(*)` is seen to conflict +// with all the others. + +#![feature(associated_types, default_type_params)] + +use std::ops::Deref; + +pub trait MyEq<Sized? U=Self> for Sized? { + fn eq(&self, u: &U) -> bool; +} + +impl<A, B> MyEq<[B]> for [A] + where A : MyEq<B> +{ + fn eq(&self, other: &[B]) -> bool { + self.len() == other.len() && + self.iter().zip(other.iter()) + .all(|(a, b)| MyEq::eq(a, b)) + } +} + +// (*) This impl conflicts with everything unless the `Output=[A]` +// constraint is considered. +impl<'a, A, B, Lhs> MyEq<[B; 0]> for Lhs + where A: MyEq<B>, Lhs: Deref<Output=[A]> +{ + fn eq(&self, other: &[B; 0]) -> bool { + MyEq::eq(&**self, other.as_slice()) + } +} + +struct DerefWithHelper<H, T> { + pub helper: H +} + +trait Helper<T> { + fn helper_borrow(&self) -> &T; +} + +impl<T> Helper<T> for Option<T> { + fn helper_borrow(&self) -> &T { + self.as_ref().unwrap() + } +} + +impl<T, H: Helper<T>> Deref for DerefWithHelper<H, T> { + type Output = T; + + fn deref(&self) -> &T { + self.helper.helper_borrow() + } +} + +pub fn check<T: MyEq>(x: T, y: T) -> bool { + let d: DerefWithHelper<Option<T>, T> = DerefWithHelper { helper: Some(x) }; + d.eq(&y) +} + +pub fn main() { +}