Skip to content

Commit cdd096b

Browse files
authored
Rollup merge of rust-lang#98028 - aticu:master, r=estebank
Add E0789 as more specific variant of E0283 Fixes rust-lang#81701 I think this should be good to go, there are only two things where I am somewhat unsure: - Is there a better way to get the fully-qualified path for the suggestion? I tried `self.tcx.def_path_str`, but that didn't seem to always give a correct path for the context. - Should all this be extracted into it's own method or is it fine where it is? r? `@estebank`
2 parents b9a008a + 3bec67f commit cdd096b

File tree

21 files changed

+350
-96
lines changed

21 files changed

+350
-96
lines changed

compiler/rustc_error_codes/src/error_codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ E0785: include_str!("./error_codes/E0785.md"),
492492
E0786: include_str!("./error_codes/E0786.md"),
493493
E0787: include_str!("./error_codes/E0787.md"),
494494
E0788: include_str!("./error_codes/E0788.md"),
495+
E0789: include_str!("./error_codes/E0789.md"),
495496
;
496497
// E0006, // merged with E0005
497498
// E0008, // cannot bind by-move into a pattern guard

compiler/rustc_error_codes/src/error_codes/E0283.md

+12-33
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,27 @@ An implementation cannot be chosen unambiguously because of lack of information.
33
Erroneous code example:
44

55
```compile_fail,E0283
6-
trait Generator {
7-
fn create() -> u32;
8-
}
9-
10-
struct Impl;
11-
12-
impl Generator for Impl {
13-
fn create() -> u32 { 1 }
14-
}
15-
16-
struct AnotherImpl;
6+
struct Foo;
177
18-
impl Generator for AnotherImpl {
19-
fn create() -> u32 { 2 }
8+
impl Into<u32> for Foo {
9+
fn into(self) -> u32 { 1 }
2010
}
2111
22-
fn main() {
23-
let cont: u32 = Generator::create();
24-
// error, impossible to choose one of Generator trait implementation
25-
// Should it be Impl or AnotherImpl, maybe something else?
26-
}
12+
let foo = Foo;
13+
let bar: u32 = foo.into() * 1u32;
2714
```
2815

2916
This error can be solved by adding type annotations that provide the missing
30-
information to the compiler. In this case, the solution is to use a concrete
31-
type:
17+
information to the compiler. In this case, the solution is to specify the
18+
trait's type parameter:
3219

3320
```
34-
trait Generator {
35-
fn create() -> u32;
36-
}
37-
38-
struct AnotherImpl;
21+
struct Foo;
3922
40-
impl Generator for AnotherImpl {
41-
fn create() -> u32 { 2 }
23+
impl Into<u32> for Foo {
24+
fn into(self) -> u32 { 1 }
4225
}
4326
44-
fn main() {
45-
let gen1 = AnotherImpl::create();
46-
47-
// if there are multiple methods with same name (different traits)
48-
let gen2 = <AnotherImpl as Generator>::create();
49-
}
27+
let foo = Foo;
28+
let bar: u32 = Into::<u32>::into(foo) * 1u32;
5029
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
You need to specify a specific implementation of the trait in order to call the
2+
method.
3+
4+
Erroneous code example:
5+
6+
```compile_fail,E0789
7+
trait Generator {
8+
fn create() -> u32;
9+
}
10+
11+
struct Impl;
12+
13+
impl Generator for Impl {
14+
fn create() -> u32 { 1 }
15+
}
16+
17+
struct AnotherImpl;
18+
19+
impl Generator for AnotherImpl {
20+
fn create() -> u32 { 2 }
21+
}
22+
23+
let cont: u32 = Generator::create();
24+
// error, impossible to choose one of Generator trait implementation
25+
// Should it be Impl or AnotherImpl, maybe something else?
26+
```
27+
28+
This error can be solved by adding type annotations that provide the missing
29+
information to the compiler. In this case, the solution is to use a concrete
30+
type:
31+
32+
```
33+
trait Generator {
34+
fn create() -> u32;
35+
}
36+
37+
struct AnotherImpl;
38+
39+
impl Generator for AnotherImpl {
40+
fn create() -> u32 { 2 }
41+
}
42+
43+
let gen1 = AnotherImpl::create();
44+
45+
// if there are multiple methods with same name (different traits)
46+
let gen2 = <AnotherImpl as Generator>::create();
47+
```

compiler/rustc_middle/src/ty/trait_def.rs

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ impl TraitImpls {
7474
pub fn blanket_impls(&self) -> &[DefId] {
7575
self.blanket_impls.as_slice()
7676
}
77+
78+
pub fn non_blanket_impls(&self) -> &FxIndexMap<SimplifiedType, Vec<DefId>> {
79+
&self.non_blanket_impls
80+
}
7781
}
7882

7983
impl<'tcx> TraitDef {

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+92
Original file line numberDiff line numberDiff line change
@@ -2104,6 +2104,98 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
21042104
);
21052105
}
21062106
}
2107+
2108+
if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
2109+
(body_id, subst.map(|subst| subst.unpack()))
2110+
{
2111+
struct FindExprBySpan<'hir> {
2112+
span: Span,
2113+
result: Option<&'hir hir::Expr<'hir>>,
2114+
}
2115+
2116+
impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> {
2117+
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
2118+
if self.span == ex.span {
2119+
self.result = Some(ex);
2120+
} else {
2121+
hir::intravisit::walk_expr(self, ex);
2122+
}
2123+
}
2124+
}
2125+
2126+
let mut expr_finder = FindExprBySpan { span, result: None };
2127+
2128+
expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);
2129+
2130+
if let Some(hir::Expr {
2131+
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), .. }
2132+
) = expr_finder.result
2133+
&& let [
2134+
..,
2135+
trait_path_segment @ hir::PathSegment {
2136+
res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)),
2137+
..
2138+
},
2139+
hir::PathSegment {
2140+
ident: assoc_item_name,
2141+
res: Some(rustc_hir::def::Res::Def(_, item_id)),
2142+
..
2143+
}
2144+
] = path.segments
2145+
&& data.trait_ref.def_id == *trait_id
2146+
&& self.tcx.trait_of_item(item_id) == Some(*trait_id)
2147+
&& !self.is_tainted_by_errors()
2148+
{
2149+
let (verb, noun) = match self.tcx.associated_item(item_id).kind {
2150+
ty::AssocKind::Const => ("refer to the", "constant"),
2151+
ty::AssocKind::Fn => ("call", "function"),
2152+
ty::AssocKind::Type => ("refer to the", "type"), // this is already covered by E0223, but this single match arm doesn't hurt here
2153+
};
2154+
2155+
// Replace the more general E0283 with a more specific error
2156+
err.cancel();
2157+
err = self.tcx.sess.struct_span_err_with_code(
2158+
span,
2159+
&format!(
2160+
"cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type",
2161+
),
2162+
rustc_errors::error_code!(E0789),
2163+
);
2164+
2165+
if let Some(local_def_id) = data.trait_ref.def_id.as_local()
2166+
&& let Some(hir::Node::Item(hir::Item { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. })) = self.tcx.hir().find_by_def_id(local_def_id)
2167+
&& let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) {
2168+
err.span_label(method_ref.span, format!("`{}::{}` defined here", trait_name, assoc_item_name));
2169+
}
2170+
2171+
err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
2172+
2173+
let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
2174+
2175+
if trait_impls.blanket_impls().is_empty()
2176+
&& let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next()
2177+
&& let Some(impl_def_id) = impl_ty.def() {
2178+
let message = if trait_impls.non_blanket_impls().len() == 1 {
2179+
"use the fully-qualified path to the only available implementation".to_string()
2180+
} else {
2181+
format!(
2182+
"use a fully-qualified path to a specific available implementation ({} found)",
2183+
trait_impls.non_blanket_impls().len()
2184+
)
2185+
};
2186+
2187+
err.multipart_suggestion(
2188+
message,
2189+
vec![
2190+
(trait_path_segment.ident.span.shrink_to_lo(), format!("<{} as ", self.tcx.def_path(impl_def_id).to_string_no_crate_verbose())),
2191+
(trait_path_segment.ident.span.shrink_to_hi(), format!(">"))
2192+
],
2193+
Applicability::MaybeIncorrect
2194+
);
2195+
}
2196+
}
2197+
};
2198+
21072199
err
21082200
}
21092201

src/test/ui/associated-consts/issue-63496.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ trait A {
22
const C: usize;
33

44
fn f() -> ([u8; A::C], [u8; A::C]);
5-
//~^ ERROR: type annotations needed
6-
//~| ERROR: type annotations needed
5+
//~^ ERROR: E0789
6+
//~| ERROR: E0789
77
}
88

99
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
1-
error[E0283]: type annotations needed
1+
error[E0789]: cannot refer to the associated constant on trait without specifying the corresponding `impl` type
22
--> $DIR/issue-63496.rs:4:21
33
|
4+
LL | const C: usize;
5+
| --------------- `A::C` defined here
6+
LL |
47
LL | fn f() -> ([u8; A::C], [u8; A::C]);
5-
| ^^^^
6-
| |
7-
| cannot infer type
8-
| help: use the fully qualified path to an implementation: `<Type as A>::C`
9-
|
10-
= note: cannot satisfy `_: A`
11-
= note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
8+
| ^^^^ cannot refer to the associated constant of trait
129

13-
error[E0283]: type annotations needed
10+
error[E0789]: cannot refer to the associated constant on trait without specifying the corresponding `impl` type
1411
--> $DIR/issue-63496.rs:4:33
1512
|
13+
LL | const C: usize;
14+
| --------------- `A::C` defined here
15+
LL |
1616
LL | fn f() -> ([u8; A::C], [u8; A::C]);
17-
| ^^^^
18-
| |
19-
| cannot infer type
20-
| help: use the fully qualified path to an implementation: `<Type as A>::C`
21-
|
22-
= note: cannot satisfy `_: A`
23-
= note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
17+
| ^^^^ cannot refer to the associated constant of trait
2418

2519
error: aborting due to 2 previous errors
2620

27-
For more information about this error, try `rustc --explain E0283`.
21+
For more information about this error, try `rustc --explain E0789`.

src/test/ui/associated-item/issue-48027.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
trait Bar {
22
const X: usize;
3-
fn return_n(&self) -> [u8; Bar::X]; //~ ERROR: type annotations needed
3+
fn return_n(&self) -> [u8; Bar::X]; //~ ERROR: E0789
44
}
55

66
impl dyn Bar {} //~ ERROR: the trait `Bar` cannot be made into an object

src/test/ui/associated-item/issue-48027.stderr

+5-9
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,15 @@ LL | const X: usize;
1313
| ^ ...because it contains this associated `const`
1414
= help: consider moving `X` to another trait
1515

16-
error[E0283]: type annotations needed
16+
error[E0789]: cannot refer to the associated constant on trait without specifying the corresponding `impl` type
1717
--> $DIR/issue-48027.rs:3:32
1818
|
19+
LL | const X: usize;
20+
| --------------- `Bar::X` defined here
1921
LL | fn return_n(&self) -> [u8; Bar::X];
20-
| ^^^^^^
21-
| |
22-
| cannot infer type
23-
| help: use the fully qualified path to an implementation: `<Type as Bar>::X`
24-
|
25-
= note: cannot satisfy `_: Bar`
26-
= note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
22+
| ^^^^^^ cannot refer to the associated constant of trait
2723

2824
error: aborting due to 2 previous errors
2925

30-
Some errors have detailed explanations: E0038, E0283.
26+
Some errors have detailed explanations: E0038, E0789.
3127
For more information about an error, try `rustc --explain E0038`.

src/test/ui/associated-types/associated-types-unconstrained.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ impl Foo for isize {
1212

1313
pub fn main() {
1414
let x: isize = Foo::bar();
15-
//~^ ERROR type annotations needed
15+
//~^ ERROR E0789
1616
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
error[E0283]: type annotations needed
1+
error[E0789]: cannot call associated function on trait without specifying the corresponding `impl` type
22
--> $DIR/associated-types-unconstrained.rs:14:20
33
|
4+
LL | fn bar() -> isize;
5+
| ------------------ `Foo::bar` defined here
6+
...
47
LL | let x: isize = Foo::bar();
5-
| ^^^^^^^^ cannot infer type
6-
|
7-
= note: cannot satisfy `_: Foo`
8+
| ^^^^^^^^ cannot call associated function of trait
89

910
error: aborting due to previous error
1011

11-
For more information about this error, try `rustc --explain E0283`.
12+
For more information about this error, try `rustc --explain E0789`.

src/test/ui/error-codes/E0283.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl Generator for AnotherImpl {
2727
}
2828

2929
fn main() {
30-
let cont: u32 = Generator::create(); //~ ERROR E0283
30+
let cont: u32 = Generator::create(); //~ ERROR E0789
3131
}
3232

3333
fn buzz() {

src/test/ui/error-codes/E0283.stderr

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
error[E0283]: type annotations needed
1+
error[E0789]: cannot call associated function on trait without specifying the corresponding `impl` type
22
--> $DIR/E0283.rs:30:21
33
|
4+
LL | fn create() -> u32;
5+
| ------------------- `Generator::create` defined here
6+
...
47
LL | let cont: u32 = Generator::create();
5-
| ^^^^^^^^^^^^^^^^^ cannot infer type
8+
| ^^^^^^^^^^^^^^^^^ cannot call associated function of trait
9+
|
10+
help: use a fully-qualified path to a specific available implementation (2 found)
611
|
7-
= note: cannot satisfy `_: Generator`
12+
LL | let cont: u32 = <::Impl as Generator>::create();
13+
| ++++++++++ +
814

915
error[E0283]: type annotations needed
1016
--> $DIR/E0283.rs:35:24
@@ -27,4 +33,5 @@ LL | let bar = <Impl as Into<T>>::into(foo_impl) * 1u32;
2733

2834
error: aborting due to 2 previous errors
2935

30-
For more information about this error, try `rustc --explain E0283`.
36+
Some errors have detailed explanations: E0283, E0789.
37+
For more information about an error, try `rustc --explain E0283`.

0 commit comments

Comments
 (0)