Skip to content

Commit 18a3cc5

Browse files
Rework collapse method to work correctly with more complex supertrait graphs
1 parent f8c51d3 commit 18a3cc5

9 files changed

+265
-21
lines changed

compiler/rustc_hir_typeck/src/method/probe.rs

+46-21
Original file line numberDiff line numberDiff line change
@@ -2112,42 +2112,67 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
21122112
self_ty: Ty<'tcx>,
21132113
probes: &[(&Candidate<'tcx>, ProbeResult)],
21142114
) -> Option<Pick<'tcx>> {
2115-
let mut child_pick = probes[0].0;
2116-
let mut supertraits: SsoHashSet<_> =
2117-
supertrait_def_ids(self.tcx, child_pick.item.trait_container(self.tcx)?).collect();
2115+
let mut child_candidate = probes[0].0;
2116+
let mut child_trait = child_candidate.item.trait_container(self.tcx)?;
2117+
let mut supertraits: SsoHashSet<_> = supertrait_def_ids(self.tcx, child_trait).collect();
2118+
2119+
let mut remaining_candidates: Vec<_> = probes[1..].iter().map(|&(p, _)| p).collect();
2120+
while !remaining_candidates.is_empty() {
2121+
let mut made_progress = false;
2122+
let mut next_round = vec![];
2123+
2124+
for remaining_candidate in remaining_candidates {
2125+
let remaining_trait = remaining_candidate.item.trait_container(self.tcx)?;
2126+
if supertraits.contains(&remaining_trait) {
2127+
made_progress = true;
2128+
continue;
2129+
}
21182130

2119-
// All other picks should be a supertrait of the `child_pick`.
2120-
// If it's not, then we update the `child_pick` and the `supertraits`
2121-
// list.
2122-
for (p, _) in &probes[1..] {
2123-
let p_container = p.item.trait_container(self.tcx)?;
2124-
if !supertraits.contains(&p_container) {
21252131
// This pick is not a supertrait of the `child_pick`.
2126-
// Check if it's a subtrait of the `child_pick`, which
2127-
// is sufficient to imply that all of the previous picks
2128-
// are also supertraits of this pick.
2129-
supertraits = supertrait_def_ids(self.tcx, p_container).collect();
2130-
if supertraits.contains(&child_pick.item.trait_container(self.tcx).unwrap()) {
2131-
child_pick = *p;
2132-
} else {
2133-
// `child_pick` is not a supertrait of this pick. Bail.
2134-
return None;
2132+
// Check if it's a subtrait of the `child_pick`, instead.
2133+
// If it is, then it must have been a subtrait of every
2134+
// other pick we've eliminated at this point. It will
2135+
// take over at this point.
2136+
let remaining_trait_supertraits: SsoHashSet<_> =
2137+
supertrait_def_ids(self.tcx, remaining_trait).collect();
2138+
if remaining_trait_supertraits.contains(&child_trait) {
2139+
child_candidate = remaining_candidate;
2140+
child_trait = remaining_trait;
2141+
supertraits = remaining_trait_supertraits;
2142+
made_progress = true;
2143+
continue;
21352144
}
2145+
2146+
// `child_pick` is not a supertrait of this pick.
2147+
// Don't bail here, since we may be comparing two supertraits
2148+
// of a common subtrait. These two supertraits won't be related
2149+
// at all, but we will pick them up next round when we find their
2150+
// child as we continue iterating in this round.
2151+
next_round.push(remaining_candidate);
2152+
}
2153+
2154+
if made_progress {
2155+
// If we've made progress, iterate again.
2156+
remaining_candidates = next_round;
2157+
} else {
2158+
// Otherwise, we must have at least two candidates which
2159+
// are not related to each other at all.
2160+
return None;
21362161
}
21372162
}
21382163

21392164
Some(Pick {
2140-
item: child_pick.item,
2165+
item: child_candidate.item,
21412166
kind: TraitPick,
2142-
import_ids: child_pick.import_ids.clone(),
2167+
import_ids: child_candidate.import_ids.clone(),
21432168
autoderefs: 0,
21442169
autoref_or_ptr_adjustment: None,
21452170
self_ty,
21462171
unstable_candidates: vec![],
21472172
shadowed_candidates: probes
21482173
.iter()
21492174
.map(|(c, _)| c.item)
2150-
.filter(|item| item.def_id != child_pick.item.def_id)
2175+
.filter(|item| item.def_id != child_candidate.item.def_id)
21512176
.collect(),
21522177
receiver_steps: None,
21532178
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ run-pass
2+
//@ check-run-results
3+
4+
#![feature(supertrait_item_shadowing)]
5+
#![warn(supertrait_item_shadowing_usage)]
6+
#![allow(dead_code)]
7+
8+
trait A {
9+
fn hello(&self) {
10+
println!("A");
11+
}
12+
}
13+
impl<T> A for T {}
14+
15+
trait B {
16+
fn hello(&self) {
17+
println!("B");
18+
}
19+
}
20+
impl<T> B for T {}
21+
22+
trait C: A + B {
23+
fn hello(&self) {
24+
println!("C");
25+
}
26+
}
27+
impl<T> C for T {}
28+
29+
fn main() {
30+
().hello();
31+
//~^ WARN trait item `hello` from `C` shadows identically named item from supertrait
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
C
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
warning: trait item `hello` from `C` shadows identically named item from supertrait
2+
--> $DIR/common-ancestor-2.rs:30:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^
6+
|
7+
note: item from `C` shadows a supertrait item
8+
--> $DIR/common-ancestor-2.rs:23:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: items from several supertraits are shadowed: `A` and `B`
13+
--> $DIR/common-ancestor-2.rs:9:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
...
18+
LL | fn hello(&self) {
19+
| ^^^^^^^^^^^^^^^
20+
note: the lint level is defined here
21+
--> $DIR/common-ancestor-2.rs:5:9
22+
|
23+
LL | #![warn(supertrait_item_shadowing_usage)]
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25+
26+
warning: 1 warning emitted
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//@ run-pass
2+
//@ check-run-results
3+
4+
#![feature(supertrait_item_shadowing)]
5+
#![warn(supertrait_item_shadowing_usage)]
6+
#![allow(dead_code)]
7+
8+
trait A {
9+
fn hello(&self) {
10+
println!("A");
11+
}
12+
}
13+
impl<T> A for T {}
14+
15+
trait B {
16+
fn hello(&self) {
17+
println!("B");
18+
}
19+
}
20+
impl<T> B for T {}
21+
22+
trait C: A + B {
23+
fn hello(&self) {
24+
println!("C");
25+
}
26+
}
27+
impl<T> C for T {}
28+
29+
// `D` extends `C` which extends `B` and `A`
30+
31+
trait D: C {
32+
fn hello(&self) {
33+
println!("D");
34+
}
35+
}
36+
impl<T> D for T {}
37+
38+
fn main() {
39+
().hello();
40+
//~^ WARN trait item `hello` from `D` shadows identically named item from supertrait
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
D
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
warning: trait item `hello` from `D` shadows identically named item from supertrait
2+
--> $DIR/common-ancestor-3.rs:39:8
3+
|
4+
LL | ().hello();
5+
| ^^^^^
6+
|
7+
note: item from `D` shadows a supertrait item
8+
--> $DIR/common-ancestor-3.rs:32:5
9+
|
10+
LL | fn hello(&self) {
11+
| ^^^^^^^^^^^^^^^
12+
note: items from several supertraits are shadowed: `A`, `B`, and `C`
13+
--> $DIR/common-ancestor-3.rs:9:5
14+
|
15+
LL | fn hello(&self) {
16+
| ^^^^^^^^^^^^^^^
17+
...
18+
LL | fn hello(&self) {
19+
| ^^^^^^^^^^^^^^^
20+
...
21+
LL | fn hello(&self) {
22+
| ^^^^^^^^^^^^^^^
23+
note: the lint level is defined here
24+
--> $DIR/common-ancestor-3.rs:5:9
25+
|
26+
LL | #![warn(supertrait_item_shadowing_usage)]
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28+
29+
warning: 1 warning emitted
30+
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,50 @@
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 - ().hello();
30+
LL + A::hello(&());
31+
|
32+
help: disambiguate the method for candidate #2
33+
|
34+
LL - ().hello();
35+
LL + B::hello(&());
36+
|
37+
help: disambiguate the method for candidate #3
38+
|
39+
LL - ().hello();
40+
LL + C::hello(&());
41+
|
42+
help: disambiguate the method for candidate #4
43+
|
44+
LL - ().hello();
45+
LL + D::hello(&());
46+
|
47+
48+
error: aborting due to 1 previous error
49+
50+
For more information about this error, try `rustc --explain E0034`.

0 commit comments

Comments
 (0)