From 4b86ebf737cbd8172369c0484e20b9b84b35360e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Oct 2023 23:08:07 +0000 Subject: [PATCH] Suggest constraining assoc types in more cases Fix #46969. --- .../infer/error_reporting/note_and_explain.rs | 102 ++++++++++-------- ...ng-assoc-type-because-of-assoc-const.fixed | 15 +++ ...ining-assoc-type-because-of-assoc-const.rs | 15 +++ ...g-assoc-type-because-of-assoc-const.stderr | 16 +++ 4 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.fixed create mode 100644 tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.rs create mode 100644 tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 24767b0bda6e6..8ecf63ec66568 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -341,39 +341,48 @@ impl Trait for X { let tcx = self.tcx; let assoc = tcx.associated_item(proj_ty.def_id); let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx); - if let Some(item) = tcx.hir().get_if_local(body_owner_def_id) { - if let Some(hir_generics) = item.generics() { - // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. - // This will also work for `impl Trait`. - let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { - let generics = tcx.generics_of(body_owner_def_id); - generics.type_param(param_ty, tcx).def_id - } else { - return false; - }; - let Some(def_id) = def_id.as_local() else { - return false; - }; - - // First look in the `where` clause, as this might be - // `fn foo(x: T) where T: Trait`. - for pred in hir_generics.bounds_for_param(def_id) { - if self.constrain_generic_bound_associated_type_structured_suggestion( - diag, - &trait_ref, - pred.bounds, - assoc, - assoc_args, - ty, - &msg, - false, - ) { - return true; - } - } + let Some(item) = tcx.hir().get_if_local(body_owner_def_id) else { + return false; + }; + let Some(hir_generics) = item.generics() else { + return false; + }; + // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. + // This will also work for `impl Trait`. + let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { + let generics = tcx.generics_of(body_owner_def_id); + generics.type_param(param_ty, tcx).def_id + } else { + return false; + }; + let Some(def_id) = def_id.as_local() else { + return false; + }; + + // First look in the `where` clause, as this might be + // `fn foo(x: T) where T: Trait`. + for pred in hir_generics.bounds_for_param(def_id) { + if self.constrain_generic_bound_associated_type_structured_suggestion( + diag, + &trait_ref, + pred.bounds, + assoc, + assoc_args, + ty, + &msg, + false, + ) { + return true; } } - false + // If associated item, look to constrain the params of the trait/impl. + let hir_id = match item { + hir::Node::ImplItem(item) => item.hir_id(), + hir::Node::TraitItem(item) => item.hir_id(), + _ => return false, + }; + let parent = tcx.hir().get_parent_item(hir_id).def_id; + self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty) } /// An associated type was expected and a different type was found. @@ -426,21 +435,26 @@ impl Trait for X { let impl_comparison = matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. }); let assoc = tcx.associated_item(proj_ty.def_id); - if !callable_scope || impl_comparison { + if impl_comparison { // We do not want to suggest calling functions when the reason of the - // type error is a comparison of an `impl` with its `trait` or when the - // scope is outside of a `Body`. + // type error is a comparison of an `impl` with its `trait`. } else { - // If we find a suitable associated function that returns the expected type, we don't - // want the more general suggestion later in this method about "consider constraining - // the associated type or calling a method that returns the associated type". - let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( - diag, - assoc.container_id(tcx), - current_method_ident, - proj_ty.def_id, - values.expected, - ); + let point_at_assoc_fn = if callable_scope + && self.point_at_methods_that_satisfy_associated_type( + diag, + assoc.container_id(tcx), + current_method_ident, + proj_ty.def_id, + values.expected, + ) { + // If we find a suitable associated function that returns the expected type, we + // don't want the more general suggestion later in this method about "consider + // constraining the associated type or calling a method that returns the associated + // type". + true + } else { + false + }; // Possibly suggest constraining the associated type to conform to the // found type. if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found) diff --git a/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.fixed b/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.fixed new file mode 100644 index 0000000000000..83321025b106b --- /dev/null +++ b/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.fixed @@ -0,0 +1,15 @@ +// run-rustfix +trait O { + type M; +} +trait U { + const N: A::M; +} +impl O for D { + type M = u8; +} +impl> U for u16 { + const N: C::M = 4u8; //~ ERROR mismatched types +} +fn main() {} + diff --git a/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.rs b/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.rs new file mode 100644 index 0000000000000..8f2501f9bda33 --- /dev/null +++ b/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.rs @@ -0,0 +1,15 @@ +// run-rustfix +trait O { + type M; +} +trait U { + const N: A::M; +} +impl O for D { + type M = u8; +} +impl U for u16 { + const N: C::M = 4u8; //~ ERROR mismatched types +} +fn main() {} + diff --git a/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.stderr b/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.stderr new file mode 100644 index 0000000000000..b104f38ce9080 --- /dev/null +++ b/tests/ui/associated-type-bounds/suggest-contraining-assoc-type-because-of-assoc-const.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/suggest-contraining-assoc-type-because-of-assoc-const.rs:12:21 + | +LL | const N: C::M = 4u8; + | ^^^ expected associated type, found `u8` + | + = note: expected associated type `::M` + found type `u8` +help: consider constraining the associated type `::M` to `u8` + | +LL | impl> U for u16 { + | ++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.