From be8d9bb98a140d4a4df762e2ea44109c2f05ce6c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 5 Feb 2015 11:48:20 -0500 Subject: [PATCH 1/4] When elaborating predicates, purge duplicates from the initial vector. Fixes #21965. --- src/librustc/middle/traits/util.rs | 13 ++++----- ...sociated-types-duplicate-binding-in-env.rs | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 src/test/run-pass/associated-types-duplicate-binding-in-env.rs diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 45ce692bb0763..6cbac7d01e7b7 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -11,12 +11,12 @@ 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, @@ -36,7 +36,7 @@ use super::{Obligation, ObligationCause, PredicateObligation, pub struct Elaborator<'cx, 'tcx:'cx> { tcx: &'cx ty::ctxt<'tcx>, stack: Vec>, - visited: HashSet>, + visited: FnvHashSet>, } struct StackEntry<'tcx> { @@ -65,14 +65,11 @@ pub fn elaborate_trait_refs<'cx, 'tcx>( pub fn elaborate_predicates<'cx, 'tcx>( tcx: &'cx ty::ctxt<'tcx>, - predicates: Vec>) + mut predicates: Vec>) -> Elaborator<'cx, 'tcx> { - let visited: HashSet> = - predicates.iter() - .map(|b| (*b).clone()) - .collect(); - + let mut visited = FnvHashSet(); + predicates.retain(|pred| visited.insert(pred.clone())); let entry = StackEntry { position: 0, predicates: predicates }; Elaborator { tcx: tcx, stack: vec![entry], visited: visited } } 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 0000000000000..62ac21879520b --- /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 or the MIT license +// , 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() -> () + where T : Foo, T : Foo +{ + ::get() +} + +fn main() { +} From 8d9bb17204c0b3b9c8ade3569ac4501fac18b885 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 5 Feb 2015 15:50:34 -0500 Subject: [PATCH 2/4] Extend the solution to encompass HRTB --- src/librustc/middle/traits/util.rs | 49 +++++++++++++++++-- ...ted-types-duplicate-binding-in-env-hrtb.rs | 9 ++++ 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/test/run-pass/associated-types-duplicate-binding-in-env-hrtb.rs diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 6cbac7d01e7b7..be8a4fbb1a821 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -22,6 +22,47 @@ use util::ppaux::Repr; use super::{Obligation, ObligationCause, PredicateObligation, VtableImpl, VtableParam, VtableImplData}; +struct PredicateSet<'a,'tcx:'a> { + tcx: &'a ty::ctxt<'tcx>, + set: FnvHashSet>, +} + +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>, - visited: FnvHashSet>, + visited: PredicateSet<'cx,'tcx>, } struct StackEntry<'tcx> { @@ -68,8 +109,8 @@ pub fn elaborate_predicates<'cx, 'tcx>( mut predicates: Vec>) -> Elaborator<'cx, 'tcx> { - let mut visited = FnvHashSet(); - predicates.retain(|pred| visited.insert(pred.clone())); + 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 } } @@ -91,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/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 0000000000000..f4b765d14b072 --- /dev/null +++ b/src/test/run-pass/associated-types-duplicate-binding-in-env-hrtb.rs @@ -0,0 +1,9 @@ +fn foo(t: T) -> i32 + where T : for<'a> Fn(&'a u8) -> i32, + T : for<'b> Fn(&'b u8) -> i32, +{ + t(&3) +} + +fn main() { +} From fab32b41676c6c9d75733ce78d8d5f455a4f27fc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 5 Feb 2015 16:24:12 -0500 Subject: [PATCH 3/4] Now that the elaboration mechanism is suppressing defaults, we can remove this overeager code that was pruning out ambig where-clause matches in trait selection. cc #21974. --- src/librustc/middle/traits/select.rs | 19 ------------------- src/test/compile-fail/issue-21974.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 src/test/compile-fail/issue-21974.rs diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index b8af91add9efb..e9909cc512c10 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/test/compile-fail/issue-21974.rs b/src/test/compile-fail/issue-21974.rs new file mode 100644 index 0000000000000..f768d6c00ecdb --- /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 or the MIT license +// , 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() { } From bbf0898013d0e2cb9655987cbef1f00569e6299a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 5 Feb 2015 17:01:11 -0500 Subject: [PATCH 4/4] Fix license --- ...sociated-types-duplicate-binding-in-env-hrtb.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 index f4b765d14b072..8b7ea61dc77e9 100644 --- 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 @@ -1,3 +1,17 @@ +// 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 or the MIT license +// , 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) -> i32 where T : for<'a> Fn(&'a u8) -> i32, T : for<'b> Fn(&'b u8) -> i32,