Skip to content

Commit 7902e88

Browse files
Rework collapse method to work correctly with more complex supertrait graphs
1 parent 94baec6 commit 7902e88

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
@@ -1725,42 +1725,67 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17251725
self_ty: Ty<'tcx>,
17261726
probes: &[(&Candidate<'tcx>, ProbeResult)],
17271727
) -> Option<Pick<'tcx>> {
1728-
let mut child_pick = probes[0].0;
1729-
let mut supertraits: SsoHashSet<_> =
1730-
supertrait_def_ids(self.tcx, child_pick.item.trait_container(self.tcx)?).collect();
1728+
let mut child_candidate = probes[0].0;
1729+
let mut child_trait = child_candidate.item.trait_container(self.tcx)?;
1730+
let mut supertraits: SsoHashSet<_> = supertrait_def_ids(self.tcx, child_trait).collect();
1731+
1732+
let mut remaining_candidates: Vec<_> = probes[1..].iter().map(|&(p, _)| p).collect();
1733+
while !remaining_candidates.is_empty() {
1734+
let mut made_progress = false;
1735+
let mut next_round = vec![];
1736+
1737+
for remaining_candidate in remaining_candidates {
1738+
let remaining_trait = remaining_candidate.item.trait_container(self.tcx)?;
1739+
if supertraits.contains(&remaining_trait) {
1740+
made_progress = true;
1741+
continue;
1742+
}
17311743

1732-
// All other picks should be a supertrait of the `child_pick`.
1733-
// If it's not, then we update the `child_pick` and the `supertraits`
1734-
// list.
1735-
for (p, _) in &probes[1..] {
1736-
let p_container = p.item.trait_container(self.tcx)?;
1737-
if !supertraits.contains(&p_container) {
17381744
// This pick is not a supertrait of the `child_pick`.
1739-
// Check if it's a subtrait of the `child_pick`, which
1740-
// is sufficient to imply that all of the previous picks
1741-
// are also supertraits of this pick.
1742-
supertraits = supertrait_def_ids(self.tcx, p_container).collect();
1743-
if supertraits.contains(&child_pick.item.trait_container(self.tcx).unwrap()) {
1744-
child_pick = *p;
1745-
} else {
1746-
// `child_pick` is not a supertrait of this pick. Bail.
1747-
return None;
1745+
// Check if it's a subtrait of the `child_pick`, instead.
1746+
// If it is, then it must have been a subtrait of every
1747+
// other pick we've eliminated at this point. It will
1748+
// take over at this point.
1749+
let remaining_trait_supertraits: SsoHashSet<_> =
1750+
supertrait_def_ids(self.tcx, remaining_trait).collect();
1751+
if remaining_trait_supertraits.contains(&child_trait) {
1752+
child_candidate = remaining_candidate;
1753+
child_trait = remaining_trait;
1754+
supertraits = remaining_trait_supertraits;
1755+
made_progress = true;
1756+
continue;
17481757
}
1758+
1759+
// `child_pick` is not a supertrait of this pick.
1760+
// Don't bail here, since we may be comparing two supertraits
1761+
// of a common subtrait. These two supertraits won't be related
1762+
// at all, but we will pick them up next round when we find their
1763+
// child as we continue iterating in this round.
1764+
next_round.push(remaining_candidate);
1765+
}
1766+
1767+
if made_progress {
1768+
// If we've made progress, iterate again.
1769+
remaining_candidates = next_round;
1770+
} else {
1771+
// Otherwise, we must have at least two candidates which
1772+
// are not related to each other at all.
1773+
return None;
17491774
}
17501775
}
17511776

17521777
Some(Pick {
1753-
item: child_pick.item,
1778+
item: child_candidate.item,
17541779
kind: TraitPick,
1755-
import_ids: child_pick.import_ids.clone(),
1780+
import_ids: child_candidate.import_ids.clone(),
17561781
autoderefs: 0,
17571782
autoref_or_ptr_adjustment: None,
17581783
self_ty,
17591784
unstable_candidates: vec![],
17601785
shadowed_candidates: probes
17611786
.iter()
17621787
.map(|(c, _)| c.item)
1763-
.filter(|item| item.def_id != child_pick.item.def_id)
1788+
.filter(|item| item.def_id != child_candidate.item.def_id)
17641789
.collect(),
17651790
})
17661791
}
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)