Skip to content

Commit

Permalink
Add E0790 as more specific variant of E0283
Browse files Browse the repository at this point in the history
  • Loading branch information
aticu committed Jul 19, 2022
1 parent 96c2df8 commit 1cbacc0
Show file tree
Hide file tree
Showing 21 changed files with 354 additions and 92 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ E0785: include_str!("./error_codes/E0785.md"),
E0786: include_str!("./error_codes/E0786.md"),
E0787: include_str!("./error_codes/E0787.md"),
E0788: include_str!("./error_codes/E0788.md"),
E0790: include_str!("./error_codes/E0790.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
41 changes: 12 additions & 29 deletions compiler/rustc_error_codes/src/error_codes/E0283.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,31 @@ An implementation cannot be chosen unambiguously because of lack of information.
Erroneous code example:

```compile_fail,E0283
trait Generator {
fn create() -> u32;
}
struct Impl;
impl Generator for Impl {
fn create() -> u32 { 1 }
}
struct Foo;
struct AnotherImpl;
impl Generator for AnotherImpl {
fn create() -> u32 { 2 }
impl Into<u32> for Foo {
fn into(self) -> u32 { 1 }
}
fn main() {
let cont: u32 = Generator::create();
// error, impossible to choose one of Generator trait implementation
// Should it be Impl or AnotherImpl, maybe something else?
let foo = Foo;
let bar: u32 = foo.into() * 1u32;
}
```

This error can be solved by adding type annotations that provide the missing
information to the compiler. In this case, the solution is to use a concrete
type:
information to the compiler. In this case, the solution is to specify the
fully-qualified method:

```
trait Generator {
fn create() -> u32;
}
struct Foo;
struct AnotherImpl;
impl Generator for AnotherImpl {
fn create() -> u32 { 2 }
impl Into<u32> for Foo {
fn into(self) -> u32 { 1 }
}
fn main() {
let gen1 = AnotherImpl::create();
// if there are multiple methods with same name (different traits)
let gen2 = <AnotherImpl as Generator>::create();
let foo = Foo;
let bar: u32 = <Foo as Into<u32>>::into(foo) * 1u32;
}
```
51 changes: 51 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0790.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
You need to specify a specific implementation of the trait in order to call the
method.

Erroneous code example:

```compile_fail,E0790
trait Generator {
fn create() -> u32;
}
struct Impl;
impl Generator for Impl {
fn create() -> u32 { 1 }
}
struct AnotherImpl;
impl Generator for AnotherImpl {
fn create() -> u32 { 2 }
}
fn main() {
let cont: u32 = Generator::create();
// error, impossible to choose one of Generator trait implementation
// Should it be Impl or AnotherImpl, maybe something else?
}
```

This error can be solved by adding type annotations that provide the missing
information to the compiler. In this case, the solution is to use a concrete
type:

```
trait Generator {
fn create() -> u32;
}
struct AnotherImpl;
impl Generator for AnotherImpl {
fn create() -> u32 { 2 }
}
fn main() {
let gen1 = AnotherImpl::create();
// if there are multiple methods with same name (different traits)
let gen2 = <AnotherImpl as Generator>::create();
}
```
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/trait_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ impl TraitImpls {
pub fn blanket_impls(&self) -> &[DefId] {
self.blanket_impls.as_slice()
}

pub fn non_blanket_impls(&self) -> &FxIndexMap<SimplifiedType, Vec<DefId>> {
&self.non_blanket_impls
}
}

impl<'tcx> TraitDef {
Expand Down
92 changes: 92 additions & 0 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,98 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
);
}
}

if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
(body_id, subst.map(|subst| subst.unpack()))
{
struct FindExprBySpan<'hir> {
span: Span,
result: Option<&'hir hir::Expr<'hir>>,
}

impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> {
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
if self.span == ex.span {
self.result = Some(ex);
} else {
hir::intravisit::walk_expr(self, ex);
}
}
}

let mut expr_finder = FindExprBySpan { span, result: None };

expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);

if let Some(hir::Expr {
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), .. }
) = expr_finder.result
&& let [
..,
trait_path_segment @ hir::PathSegment {
res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)),
..
},
hir::PathSegment {
ident: assoc_item_name,
res: Some(rustc_hir::def::Res::Def(_, item_id)),
..
}
] = path.segments
&& data.trait_ref.def_id == *trait_id
&& self.tcx.trait_of_item(item_id) == Some(*trait_id)
&& !self.is_tainted_by_errors()
{
let (verb, noun) = match self.tcx.associated_item(item_id).kind {
ty::AssocKind::Const => ("refer to the", "constant"),
ty::AssocKind::Fn => ("call", "function"),
ty::AssocKind::Type => ("refer to the", "type"), // this is already covered by E0223, but this single match arm doesn't hurt here
};

// Replace the more general E0283 with a more specific error
err.cancel();
err = self.tcx.sess.struct_span_err_with_code(
span,
&format!(
"cannot {verb} associated {noun} on trait without specifying the corresponding `impl` type",
),
rustc_errors::error_code!(E0790),
);

if let Some(local_def_id) = data.trait_ref.def_id.as_local()
&& 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)
&& let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) {
err.span_label(method_ref.span, format!("`{}::{}` defined here", trait_name, assoc_item_name));
}

err.span_label(span, format!("cannot {verb} associated {noun} of trait"));

let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);

if trait_impls.blanket_impls().is_empty()
&& let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next()
&& let Some(impl_def_id) = impl_ty.def() {
let message = if trait_impls.non_blanket_impls().len() == 1 {
"use the fully-qualified path to the only available implementation".to_string()
} else {
format!(
"use a fully-qualified path to a specific available implementation ({} found)",
trait_impls.non_blanket_impls().len()
)
};

err.multipart_suggestion(
message,
vec![
(trait_path_segment.ident.span.shrink_to_lo(), format!("<{} as ", self.tcx.def_path(impl_def_id).to_string_no_crate_verbose())),
(trait_path_segment.ident.span.shrink_to_hi(), format!(">"))
],
Applicability::MaybeIncorrect
);
}
}
};

err
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/associated-consts/issue-63496.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ trait A {
const C: usize;

fn f() -> ([u8; A::C], [u8; A::C]);
//~^ ERROR: type annotations needed
//~| ERROR: type annotations needed
//~^ ERROR: E0790
//~| ERROR: E0790
}

fn main() {}
28 changes: 11 additions & 17 deletions src/test/ui/associated-consts/issue-63496.stderr
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
error[E0283]: type annotations needed
error[E0790]: cannot refer to the associated constant on trait without specifying the corresponding `impl` type
--> $DIR/issue-63496.rs:4:21
|
LL | const C: usize;
| --------------- `A::C` defined here
LL |
LL | fn f() -> ([u8; A::C], [u8; A::C]);
| ^^^^
| |
| cannot infer type
| help: use the fully qualified path to an implementation: `<Type as A>::C`
|
= note: cannot satisfy `_: A`
= note: associated constants cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
| ^^^^ cannot refer to the associated constant of trait

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

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0283`.
For more information about this error, try `rustc --explain E0790`.
2 changes: 1 addition & 1 deletion src/test/ui/associated-item/issue-48027.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
trait Bar {
const X: usize;
fn return_n(&self) -> [u8; Bar::X]; //~ ERROR: type annotations needed
fn return_n(&self) -> [u8; Bar::X]; //~ ERROR: E0790
}

impl dyn Bar {} //~ ERROR: the trait `Bar` cannot be made into an object
Expand Down
14 changes: 5 additions & 9 deletions src/test/ui/associated-item/issue-48027.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,15 @@ LL | const X: usize;
| ^ ...because it contains this associated `const`
= help: consider moving `X` to another trait

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

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0038, E0283.
Some errors have detailed explanations: E0038, E0790.
For more information about an error, try `rustc --explain E0038`.
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ impl Foo for isize {

pub fn main() {
let x: isize = Foo::bar();
//~^ ERROR type annotations needed
//~^ ERROR E0790
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
error[E0283]: type annotations needed
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
--> $DIR/associated-types-unconstrained.rs:14:20
|
LL | fn bar() -> isize;
| ------------------ `Foo::bar` defined here
...
LL | let x: isize = Foo::bar();
| ^^^^^^^^ cannot infer type
|
= note: cannot satisfy `_: Foo`
| ^^^^^^^^ cannot call associated function of trait

error: aborting due to previous error

For more information about this error, try `rustc --explain E0283`.
For more information about this error, try `rustc --explain E0790`.
2 changes: 1 addition & 1 deletion src/test/ui/error-codes/E0283.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl Generator for AnotherImpl {
}

fn main() {
let cont: u32 = Generator::create(); //~ ERROR E0283
let cont: u32 = Generator::create(); //~ ERROR E0790
}

fn buzz() {
Expand Down
15 changes: 11 additions & 4 deletions src/test/ui/error-codes/E0283.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
error[E0283]: type annotations needed
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
--> $DIR/E0283.rs:30:21
|
LL | fn create() -> u32;
| ------------------- `Generator::create` defined here
...
LL | let cont: u32 = Generator::create();
| ^^^^^^^^^^^^^^^^^ cannot infer type
| ^^^^^^^^^^^^^^^^^ cannot call associated function of trait
|
help: use a fully-qualified path to a specific available implementation (2 found)
|
= note: cannot satisfy `_: Generator`
LL | let cont: u32 = <::Impl as Generator>::create();
| ++++++++++ +

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

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0283`.
Some errors have detailed explanations: E0283, E0790.
For more information about an error, try `rustc --explain E0283`.
Loading

0 comments on commit 1cbacc0

Please sign in to comment.