diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2022-07-19 13:30:44 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-19 13:30:44 +0200 |
| commit | bfefd587c081305ab81074fee1ff7fd26b058ef6 (patch) | |
| tree | 3da347d4655a970ff0ece525cafe8bf9bba58e79 /compiler | |
| parent | 4603ac31b0655793a82f110f544dc1c6abc57bb7 (diff) | |
| parent | 38ea23558eca629d668208e368dfa711b8229192 (diff) | |
| download | rust-bfefd587c081305ab81074fee1ff7fd26b058ef6.tar.gz rust-bfefd587c081305ab81074fee1ff7fd26b058ef6.zip | |
Rollup merge of #98028 - aticu:master, r=estebank
Add E0790 as more specific variant of E0283 Fixes #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`
Diffstat (limited to 'compiler')
5 files changed, 156 insertions, 33 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 977318b8589..97766b8368a 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -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 diff --git a/compiler/rustc_error_codes/src/error_codes/E0283.md b/compiler/rustc_error_codes/src/error_codes/E0283.md index 6885f9a486d..79d2c8204f9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0283.md +++ b/compiler/rustc_error_codes/src/error_codes/E0283.md @@ -3,48 +3,27 @@ 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 AnotherImpl; +struct Foo; -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 +trait's type parameter: ``` -trait Generator { - fn create() -> u32; -} - -struct AnotherImpl; +struct Foo; -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 = Into::<u32>::into(foo) * 1u32; ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0790.md b/compiler/rustc_error_codes/src/error_codes/E0790.md new file mode 100644 index 00000000000..2aee9dfbdbd --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0790.md @@ -0,0 +1,47 @@ +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 } +} + +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 } +} + +let gen1 = AnotherImpl::create(); + +// if there are multiple methods with same name (different traits) +let gen2 = <AnotherImpl as Generator>::create(); +``` diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 826c16dda4a..541dace5cc2 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -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 { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 8a679ca005f..585159e309f 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -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 } |
