Skip to content

Commit 095157c

Browse files
Rework collapse method to work correctly with more complex supertrait graphs
1 parent ef8e725 commit 095157c

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
@@ -1782,42 +1782,67 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
17821782
self_ty: Ty<'tcx>,
17831783
probes: &[(&Candidate<'tcx>, ProbeResult)],
17841784
) -> Option<Pick<'tcx>> {
1785-
let mut child_pick = probes[0].0;
1786-
let mut supertraits: SsoHashSet<_> =
1787-
supertrait_def_ids(self.tcx, child_pick.item.trait_container(self.tcx)?).collect();
1785+
let mut child_candidate = probes[0].0;
1786+
let mut child_trait = child_candidate.item.trait_container(self.tcx)?;
1787+
let mut supertraits: SsoHashSet<_> = supertrait_def_ids(self.tcx, child_trait).collect();
1788+
1789+
let mut remaining_candidates: Vec<_> = probes[1..].iter().map(|&(p, _)| p).collect();
1790+
while !remaining_candidates.is_empty() {
1791+
let mut made_progress = false;
1792+
let mut next_round = vec![];
1793+
1794+
for remaining_candidate in remaining_candidates {
1795+
let remaining_trait = remaining_candidate.item.trait_container(self.tcx)?;
1796+
if supertraits.contains(&remaining_trait) {
1797+
made_progress = true;
1798+
continue;
1799+
}
17881800

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

18091834
Some(Pick {
1810-
item: child_pick.item,
1835+
item: child_candidate.item,
18111836
kind: TraitPick,
1812-
import_ids: child_pick.import_ids.clone(),
1837+
import_ids: child_candidate.import_ids.clone(),
18131838
autoderefs: 0,
18141839
autoref_or_ptr_adjustment: None,
18151840
self_ty,
18161841
unstable_candidates: vec![],
18171842
shadowed_candidates: probes
18181843
.iter()
18191844
.map(|(c, _)| c.item)
1820-
.filter(|item| item.def_id != child_pick.item.def_id)
1845+
.filter(|item| item.def_id != child_candidate.item.def_id)
18211846
.collect(),
18221847
})
18231848
}
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)