about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2015-02-06 05:41:55 +0530
committerManish Goregaokar <manishsmail@gmail.com>2015-02-06 16:21:13 +0530
commit703364f2144c4055e5851fc5400625f086210f27 (patch)
treecb0ed5e6bc0c26ced51e9d90f5b3e893167d3878
parentdfc92656e5551d6fd9b163da7a9bb2107d51ecce (diff)
parentbbf0898013d0e2cb9655987cbef1f00569e6299a (diff)
Rollup merge of #21968 - nikomatsakis:issue-21965-duplicate-preds-in-env, r=pnkfelix
 We were already building a hashset to check for duplicates, but we assumed that the initial vector had no duplicates. Fixes #21965.

r? @pnkfelix
-rw-r--r--src/librustc/middle/traits/select.rs19
-rw-r--r--src/librustc/middle/traits/util.rs56
-rw-r--r--src/test/compile-fail/issue-21974.rs28
-rw-r--r--src/test/run-pass/associated-types-duplicate-binding-in-env-hrtb.rs23
-rw-r--r--src/test/run-pass/associated-types-duplicate-binding-in-env.rs27
5 files changed, 125 insertions, 28 deletions
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index 39eb52e5adc..f00781fd65c 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -1335,25 +1335,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // the where clauses are in scope.
                 true
             }
-            (&ParamCandidate(ref bound1), &ParamCandidate(ref bound2)) => {
-                self.infcx.probe(|_| {
-                    let bound1 =
-                        project::normalize_with_depth(self,
-                                                      stack.obligation.cause.clone(),
-                                                      stack.obligation.recursion_depth+1,
-                                                      bound1);
-                    let bound2 =
-                        project::normalize_with_depth(self,
-                                                      stack.obligation.cause.clone(),
-                                                      stack.obligation.recursion_depth+1,
-                                                      bound2);
-                    let origin =
-                        infer::RelateOutputImplTypes(stack.obligation.cause.span);
-                    self.infcx
-                        .sub_poly_trait_refs(false, origin, bound1.value, bound2.value)
-                        .is_ok()
-                })
-            }
             _ => {
                 false
             }
diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs
index 5180b8379ea..573efa72756 100644
--- a/src/librustc/middle/traits/util.rs
+++ b/src/librustc/middle/traits/util.rs
@@ -11,17 +11,58 @@
 use middle::subst::{Substs, VecPerParamSpace};
 use middle::infer::InferCtxt;
 use middle::ty::{self, Ty, AsPredicate, ToPolyTraitRef};
-use std::collections::HashSet;
 use std::fmt;
 use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::Span;
 use util::common::ErrorReported;
+use util::nodemap::FnvHashSet;
 use util::ppaux::Repr;
 
 use super::{Obligation, ObligationCause, PredicateObligation,
             VtableImpl, VtableParam, VtableImplData};
 
+struct PredicateSet<'a,'tcx:'a> {
+    tcx: &'a ty::ctxt<'tcx>,
+    set: FnvHashSet<ty::Predicate<'tcx>>,
+}
+
+impl<'a,'tcx> PredicateSet<'a,'tcx> {
+    fn new(tcx: &'a ty::ctxt<'tcx>) -> PredicateSet<'a,'tcx> {
+        PredicateSet { tcx: tcx, set: FnvHashSet() }
+    }
+
+    fn insert(&mut self, pred: &ty::Predicate<'tcx>) -> bool {
+        // We have to be careful here because we want
+        //
+        //    for<'a> Foo<&'a int>
+        //
+        // and
+        //
+        //    for<'b> Foo<&'b int>
+        //
+        // to be considered equivalent. So normalize all late-bound
+        // regions before we throw things into the underlying set.
+        let normalized_pred = match *pred {
+            ty::Predicate::Trait(ref data) =>
+                ty::Predicate::Trait(ty::anonymize_late_bound_regions(self.tcx, data)),
+
+            ty::Predicate::Equate(ref data) =>
+                ty::Predicate::Equate(ty::anonymize_late_bound_regions(self.tcx, data)),
+
+            ty::Predicate::RegionOutlives(ref data) =>
+                ty::Predicate::RegionOutlives(ty::anonymize_late_bound_regions(self.tcx, data)),
+
+            ty::Predicate::TypeOutlives(ref data) =>
+                ty::Predicate::TypeOutlives(ty::anonymize_late_bound_regions(self.tcx, data)),
+
+            ty::Predicate::Projection(ref data) =>
+                ty::Predicate::Projection(ty::anonymize_late_bound_regions(self.tcx, data)),
+        };
+        self.set.insert(normalized_pred)
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // `Elaboration` iterator
 ///////////////////////////////////////////////////////////////////////////
@@ -36,7 +77,7 @@ use super::{Obligation, ObligationCause, PredicateObligation,
 pub struct Elaborator<'cx, 'tcx:'cx> {
     tcx: &'cx ty::ctxt<'tcx>,
     stack: Vec<StackEntry<'tcx>>,
-    visited: HashSet<ty::Predicate<'tcx>>,
+    visited: PredicateSet<'cx,'tcx>,
 }
 
 struct StackEntry<'tcx> {
@@ -65,14 +106,11 @@ pub fn elaborate_trait_refs<'cx, 'tcx>(
 
 pub fn elaborate_predicates<'cx, 'tcx>(
     tcx: &'cx ty::ctxt<'tcx>,
-    predicates: Vec<ty::Predicate<'tcx>>)
+    mut predicates: Vec<ty::Predicate<'tcx>>)
     -> Elaborator<'cx, 'tcx>
 {
-    let visited: HashSet<ty::Predicate<'tcx>> =
-        predicates.iter()
-                  .map(|b| (*b).clone())
-                  .collect();
-
+    let mut visited = PredicateSet::new(tcx);
+    predicates.retain(|pred| visited.insert(pred));
     let entry = StackEntry { position: 0, predicates: predicates };
     Elaborator { tcx: tcx, stack: vec![entry], visited: visited }
 }
@@ -94,7 +132,7 @@ impl<'cx, 'tcx> Elaborator<'cx, 'tcx> {
                 // recursion in some cases.  One common case is when
                 // people define `trait Sized: Sized { }` rather than `trait
                 // Sized { }`.
-                predicates.retain(|r| self.visited.insert(r.clone()));
+                predicates.retain(|r| self.visited.insert(r));
 
                 self.stack.push(StackEntry { position: 0,
                                              predicates: predicates });
diff --git a/src/test/compile-fail/issue-21974.rs b/src/test/compile-fail/issue-21974.rs
new file mode 100644
index 00000000000..f768d6c00ec
--- /dev/null
+++ b/src/test/compile-fail/issue-21974.rs
@@ -0,0 +1,28 @@
+// 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 (for now) we report an ambiguity error here, because
+// specific trait relationships are ignored for the purposes of trait
+// matching. This behavior should likely be improved such that this
+// test passes. See #21974 for more details.
+
+trait Foo {
+    fn foo(self);
+}
+
+fn foo<'a,'b,T>(x: &'a T, y: &'b T)
+    where &'a T : Foo,
+          &'b T : Foo
+{
+    x.foo(); //~ ERROR type annotations required
+    y.foo();
+}
+
+fn main() { }
diff --git a/src/test/run-pass/associated-types-duplicate-binding-in-env-hrtb.rs b/src/test/run-pass/associated-types-duplicate-binding-in-env-hrtb.rs
new file mode 100644
index 00000000000..8b7ea61dc77
--- /dev/null
+++ b/src/test/run-pass/associated-types-duplicate-binding-in-env-hrtb.rs
@@ -0,0 +1,23 @@
+// 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 we do not report ambiguities when equivalent predicates
+// (modulo bound lifetime names) appears in the environment
+// twice. Issue #21965.
+
+fn foo<T>(t: T) -> i32
+    where T : for<'a> Fn(&'a u8) -> i32,
+          T : for<'b> Fn(&'b u8) -> i32,
+{
+    t(&3)
+}
+
+fn main() {
+}
diff --git a/src/test/run-pass/associated-types-duplicate-binding-in-env.rs b/src/test/run-pass/associated-types-duplicate-binding-in-env.rs
new file mode 100644
index 00000000000..62ac2187952
--- /dev/null
+++ b/src/test/run-pass/associated-types-duplicate-binding-in-env.rs
@@ -0,0 +1,27 @@
+// 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 we do not report ambiguities when the same predicate
+// appears in the environment twice. Issue #21965.
+
+trait Foo {
+    type B;
+
+    fn get() -> Self::B;
+}
+
+fn foo<T>() -> ()
+    where T : Foo<B=()>, T : Foo<B=()>
+{
+    <T as Foo>::get()
+}
+
+fn main() {
+}