Skip to content

Commit 069e9d2

Browse files
Rework collapse method to work correctly with more complex supertrait graphs
1 parent b1b8aeb commit 069e9d2

9 files changed

+251
-21
lines changed

compiler/rustc_hir_typeck/src/method/probe.rs

+46-21
Original file line numberDiff line numberDiff line change
@@ -1779,42 +1779,67 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17791779
self_ty: Ty<'tcx>,
17801780
probes: &[(&Candidate<'tcx>, ProbeResult)],
17811781
) -> Option<Pick<'tcx>> {
1782-
let mut child_pick = probes[0].0;
1783-
let mut supertraits: SsoHashSet<_> =
1784-
supertrait_def_ids(self.tcx, child_pick.item.trait_container(self.tcx)?).collect();
1782+
let mut child_candidate = probes[0].0;
1783+
let mut child_trait = child_candidate.item.trait_container(self.tcx)?;
1784+
let mut supertraits: SsoHashSet<_> = supertrait_def_ids(self.tcx, child_trait).collect();
1785+
1786+
let mut remaining_candidates: Vec<_> = probes[1..].iter().map(|&(p, _)| p).collect();
1787+
while !remaining_candidates.is_empty() {
1788+
let mut made_progress = false;
1789+
let mut next_round = vec![];
1790+
1791+
for remaining_candidate in remaining_candidates {
1792+
let remaining_trait = remaining_candidate.item.trait_container(self.tcx)?;
1793+
if supertraits.contains(&remaining_trait) {
1794+
made_progress = true;
1795+
continue;
1796+
}
17851797

1786-
// All other picks should be a supertrait of the `child_pick`.
1787-
// If it's not, then we update the `child_pick` and the `supertraits`
1788-
// list.
1789-
for (p, _) in &probes[1..] {
1790-
let p_container = p.item.trait_container(self.tcx)?;
1791-
if !supertraits.contains(&p_container) {
17921798
// This pick is not a supertrait of the `child_pick`.
1793-
// Check if it's a subtrait of the `child_pick`, which
1794-
// is sufficient to imply that all of the previous picks
1795-
// are also supertraits of this pick.
1796-
supertraits = supertrait_def_ids(self.tcx, p_container).collect();
1797-
if supertraits.contains(&child_pick.item.trait_container(self.tcx).unwrap()) {
1798-
child_pick = *p;
1799-
} else {
1800-
// `child_pick` is not a supertrait of this pick. Bail.
1801-
return None;
1799+
// Check if it's a subtrait of the `child_pick`, instead.
1800+
// If it is, then it must have been a subtrait of every
1801+
// other pick we've eliminated at this point. It will
1802+
// take over at this point.
1803+
let remaining_trait_supertraits: SsoHashSet<_> =
1804+
supertrait_def_ids(self.tcx, remaining_trait).collect();
1805+
if remaining_trait_supertraits.contains(&child_trait) {
1806+
child_candidate = remaining_candidate;
1807+
child_trait = remaining_trait;
1808+
supertraits = remaining_trait_supertraits;
1809+
made_progress = true;
1810+
continue;
18021811
}
1812+
1813+
// `child_pick` is not a supertrait of this pick.
1814+
// Don't bail here, since we may be comparing two supertraits
1815+
// of a common subtrait. These two supertraits won't be related
1816+
// at all, but we will pick them up next round when we find their
1817+
// child as we continue iterating in this round.
1818+
next_round.push(remaining_candidate);
1819+
}
1820+
1821+
if made_progress {
1822+
// If we've made progress, iterate again.
1823+
remaining_candidates = next_round;
1824+
} else {
1825+
// Otherwise, we must have at least two candidates which
1826+
// are not related to each other at all.
1827+
return None;
18031828
}
18041829
}
18051830

18061831
Some(Pick {
1807-
item: child_pick.item,
1832+
item: child_candidate.item,
18081833
kind: TraitPick,
1809-
import_ids: child_pick.import_ids.clone(),
1834+
import_ids: child_candidate.import_ids.clone(),
18101835
autoderefs: 0,
18111836
autoref_or_ptr_adjustment: None,
18121837
self_ty,
18131838
unstable_candidates: vec![],
18141839
shadowed_candidates: probes
18151840
.iter()
18161841
.map(|(c, _)| c.item)
1817-
.filter(|item| item.def_id != child_pick.item.def_id)
1842+
.filter(|item| item.def_id != child_candidate.item.def_id)
18181843
.collect(),
18191844
})
18201845
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ run-pass
2+
//@ check-run-results
3+
4+
#![feature(supertrait_item_shadowing)]
5+
#![allow(dead_code)]
6+
7+
trait A {
8+
fn hello(&self) {
9+
println!("A");
10+
}
11+
}
12+
impl<T> A for T {}
13+
14+
trait B {
15+
fn hello(&self) {
16+
println!("B");
17+
}
18+
}
19+
impl<T> B for T {}
20+
21+
trait C: A + B {
22+
fn hello(&self) {
23+
println!("C");
24+
}
25+
}
26+
impl<T> C for T {}
27+
28+
fn main() {
29+
().hello();
30+
//~^ WARN trait method `hello` from `C` shadows identically named method from supertrait
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
C
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
warning: trait method `hello` from `C` shadows identically named method from supertrait
2+
--> $DIR/common-ancestor-2.rs:29:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^
6+
|
7+
note: method from `C` shadows a supertrait method
8+
--> $DIR/common-ancestor-2.rs:22:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: methods from several supertraits are shadowed: `A` and `B`
13+
--> $DIR/common-ancestor-2.rs:8:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
...
18+
LL | fn hello(&self) {
19+
| ^^^^^^^^^^^^^^^
20+
= note: `#[warn(supertrait_item_shadowing)]` on by default
21+
22+
warning: 1 warning emitted
23+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//@ run-pass
2+
//@ check-run-results
3+
4+
#![feature(supertrait_item_shadowing)]
5+
#![allow(dead_code)]
6+
7+
trait A {
8+
fn hello(&self) {
9+
println!("A");
10+
}
11+
}
12+
impl<T> A for T {}
13+
14+
trait B {
15+
fn hello(&self) {
16+
println!("B");
17+
}
18+
}
19+
impl<T> B for T {}
20+
21+
trait C: A + B {
22+
fn hello(&self) {
23+
println!("C");
24+
}
25+
}
26+
impl<T> C for T {}
27+
28+
// `D` extends `C` which extends `B` and `A`
29+
30+
trait D: C {
31+
fn hello(&self) {
32+
println!("D");
33+
}
34+
}
35+
impl<T> D for T {}
36+
37+
fn main() {
38+
().hello();
39+
//~^ WARN trait method `hello` from `D` shadows identically named method from supertrait
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
D
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
warning: trait method `hello` from `D` shadows identically named method from supertrait
2+
--> $DIR/common-ancestor-3.rs:38:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^
6+
|
7+
note: method from `D` shadows a supertrait method
8+
--> $DIR/common-ancestor-3.rs:31:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: methods from several supertraits are shadowed: `A`, `B`, and `C`
13+
--> $DIR/common-ancestor-3.rs:8:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
...
18+
LL | fn hello(&self) {
19+
| ^^^^^^^^^^^^^^^
20+
...
21+
LL | fn hello(&self) {
22+
| ^^^^^^^^^^^^^^^
23+
= note: `#[warn(supertrait_item_shadowing)]` on by default
24+
25+
warning: 1 warning emitted
26+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![feature(supertrait_item_shadowing)]
2+
3+
trait A {
4+
fn hello(&self) {
5+
println!("A");
6+
}
7+
}
8+
impl<T> A for T {}
9+
10+
trait B {
11+
fn hello(&self) {
12+
println!("B");
13+
}
14+
}
15+
impl<T> B for T {}
16+
17+
trait C: A + B {
18+
fn hello(&self) {
19+
println!("C");
20+
}
21+
}
22+
impl<T> C for T {}
23+
24+
// Since `D` is not a subtrait of `C`,
25+
// we have no obvious lower bound.
26+
27+
trait D: B {
28+
fn hello(&self) {
29+
println!("D");
30+
}
31+
}
32+
impl<T> D for T {}
33+
34+
fn main() {
35+
().hello();
36+
//~^ ERROR multiple applicable items in scope
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error[E0034]: multiple applicable items in scope
2+
--> $DIR/no-common-ancestor-2.rs:35:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^ multiple `hello` found
6+
|
7+
note: candidate #1 is defined in an impl of the trait `A` for the type `T`
8+
--> $DIR/no-common-ancestor-2.rs:4:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: candidate #2 is defined in an impl of the trait `B` for the type `T`
13+
--> $DIR/no-common-ancestor-2.rs:11:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
note: candidate #3 is defined in an impl of the trait `C` for the type `T`
18+
--> $DIR/no-common-ancestor-2.rs:18:5
19+
|
20+
LL | fn hello(&self) {
21+
| ^^^^^^^^^^^^^^^
22+
note: candidate #4 is defined in an impl of the trait `D` for the type `T`
23+
--> $DIR/no-common-ancestor-2.rs:28:5
24+
|
25+
LL | fn hello(&self) {
26+
| ^^^^^^^^^^^^^^^
27+
help: disambiguate the method for candidate #1
28+
|
29+
LL | A::hello(&());
30+
| ~~~~~~~~~~~~~
31+
help: disambiguate the method for candidate #2
32+
|
33+
LL | B::hello(&());
34+
| ~~~~~~~~~~~~~
35+
help: disambiguate the method for candidate #3
36+
|
37+
LL | C::hello(&());
38+
| ~~~~~~~~~~~~~
39+
help: disambiguate the method for candidate #4
40+
|
41+
LL | D::hello(&());
42+
| ~~~~~~~~~~~~~
43+
44+
error: aborting due to 1 previous error
45+
46+
For more information about this error, try `rustc --explain E0034`.

0 commit comments

Comments
 (0)