Skip to content

Commit 415e6b8

Browse files
committed
Eliminate assoc type projection predicate candidate duplicates
When projecting associate types for a trait's default methods, the trait itself was added to the predicate candidate list twice: one from parameter environment, the other from trait definition. Then the duplicates were deemed as code ambiguity and the compiler rejected the code. Simply checking and dropping the duplicates solves the issue. Closes rust-lang#22036
1 parent d4a66e9 commit 415e6b8

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

src/librustc/middle/traits/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ mod util;
6363
/// either identifying an `impl` (e.g., `impl Eq for int`) that
6464
/// provides the required vtable, or else finding a bound that is in
6565
/// scope. The eventual result is usually a `Selection` (defined below).
66-
#[derive(Clone)]
66+
#[derive(Clone, PartialEq, Eq)]
6767
pub struct Obligation<'tcx, T> {
6868
pub cause: ObligationCause<'tcx>,
6969
pub recursion_depth: uint,
@@ -74,7 +74,7 @@ pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
7474
pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
7575

7676
/// Why did we incur this obligation? Used for error reporting.
77-
#[derive(Clone)]
77+
#[derive(Clone, PartialEq, Eq)]
7878
pub struct ObligationCause<'tcx> {
7979
pub span: Span,
8080

@@ -89,7 +89,7 @@ pub struct ObligationCause<'tcx> {
8989
pub code: ObligationCauseCode<'tcx>
9090
}
9191

92-
#[derive(Clone)]
92+
#[derive(Clone, PartialEq, Eq)]
9393
pub enum ObligationCauseCode<'tcx> {
9494
/// Not well classified or should be obvious from span.
9595
MiscObligation,
@@ -129,7 +129,7 @@ pub enum ObligationCauseCode<'tcx> {
129129
CompareImplMethodObligation,
130130
}
131131

132-
#[derive(Clone)]
132+
#[derive(Clone, PartialEq, Eq)]
133133
pub struct DerivedObligationCause<'tcx> {
134134
/// The trait reference of the parent obligation that led to the
135135
/// current obligation. Note that only trait obligations lead to
@@ -251,7 +251,7 @@ pub enum Vtable<'tcx, N> {
251251
/// is `Obligation`, as one might expect. During trans, however, this
252252
/// is `()`, because trans only requires a shallow resolution of an
253253
/// impl, and nested obligations are satisfied later.
254-
#[derive(Clone)]
254+
#[derive(Clone, PartialEq, Eq)]
255255
pub struct VtableImplData<'tcx, N> {
256256
pub impl_def_id: ast::DefId,
257257
pub substs: subst::Substs<'tcx>,

src/librustc/middle/traits/project.rs

+20
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub struct MismatchedProjectionTypes<'tcx> {
5454
pub err: ty::type_err<'tcx>
5555
}
5656

57+
#[derive(PartialEq, Eq)]
5758
enum ProjectionTyCandidate<'tcx> {
5859
ParamEnv(ty::PolyProjectionPredicate<'tcx>),
5960
Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
@@ -481,6 +482,25 @@ fn project_type<'cx,'tcx>(
481482

482483
// We probably need some winnowing logic similar to select here.
483484

485+
// Drop duplicates.
486+
//
487+
// Note: `candidates.vec` seems to be on the critical path of the
488+
// compiler. Replacing it with an hash set was also tried, which would
489+
// render the following dedup unnecessary. It led to cleaner code but
490+
// prolonged compiling time of `librustc` from 5m30s to 6m in one test, or
491+
// ~9% performance lost.
492+
if candidates.vec.len() > 1 {
493+
let mut i = 0;
494+
while i < candidates.vec.len() {
495+
let has_dup = (0..i).any(|j| candidates.vec[i] == candidates.vec[j]);
496+
if has_dup {
497+
candidates.vec.swap_remove(i);
498+
} else {
499+
i += 1;
500+
}
501+
}
502+
}
503+
484504
if candidates.ambiguous || candidates.vec.len() > 1 {
485505
return Err(ProjectionTyError::TooManyCandidates);
486506
}

src/test/run-pass/issue-22036.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait DigitCollection: Sized {
12+
type Iter: Iterator<Item = u8>;
13+
fn digit_iter(self) -> Self::Iter;
14+
15+
fn digit_sum(self) -> u32 {
16+
self.digit_iter()
17+
.map(|digit: u8| digit as u32)
18+
.fold(0, |sum, digit| sum + digit)
19+
}
20+
}
21+
22+
impl<I> DigitCollection for I where I: Iterator<Item=u8> {
23+
type Iter = I;
24+
25+
fn digit_iter(self) -> I {
26+
self
27+
}
28+
}
29+
30+
fn main() {
31+
let xs = vec![1u8, 2, 3, 4, 5];
32+
assert_eq!(xs.into_iter().digit_sum(), 15);
33+
}

0 commit comments

Comments
 (0)