diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 811833bca8031..1ae53a77adc56 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -11,13 +11,12 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint}; -use rustc_middle::ty::{ - self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor, -}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _; use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; use std::ops::ControlFlow; @@ -266,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), Err(errors) => { let source_map = self.tcx.sess.source_map(); - let (mut err, missing_trait, use_output) = match is_assign { + let (mut err, missing_trait, _use_output) = match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( self.tcx.sess, @@ -449,39 +448,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted } else if let [ty] = &visitor.0[..] { - if let ty::Param(p) = *ty.kind() { - // Check if the method would be found if the type param wasn't - // involved. If so, it means that adding a trait bound to the param is - // enough. Otherwise we do not give the suggestion. - let mut eraser = TypeParamEraser(self, expr.span); - let needs_bound = self - .lookup_op_method( - eraser.fold_ty(lhs_ty), - Some(eraser.fold_ty(rhs_ty)), - Some(rhs_expr), - Op::Binary(op, is_assign), - ) - .is_ok(); - if needs_bound { - suggest_constraining_param( - self.tcx, - self.body_id, + // Look for a TraitPredicate in the Fulfillment errors, + // and use it to generate a suggestion. + // + // Note that lookup_op_method must be called again but + // with a specific rhs_ty instead of a placeholder so + // the resulting predicate generates a more specific + // suggestion for the user. + let errors = self + .lookup_op_method( + lhs_ty, + Some(rhs_ty), + Some(rhs_expr), + Op::Binary(op, is_assign), + ) + .unwrap_err(); + let predicates = errors + .into_iter() + .filter_map(|error| error.obligation.predicate.to_opt_poly_trait_pred()) + .collect::>(); + if !predicates.is_empty() { + for pred in predicates { + self.infcx.suggest_restricting_param_bound( &mut err, - *ty, - rhs_ty, - missing_trait, - p, - use_output, + pred, + self.body_id, ); - } else if *ty != lhs_ty { - // When we know that a missing bound is responsible, we don't show - // this note as it is redundant. - err.note(&format!( - "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" - )); } - } else { - bug!("type param visitor stored a non type param: {:?}", ty.kind()); + } else if *ty != lhs_ty { + // When we know that a missing bound is responsible, we don't show + // this note as it is redundant. + err.note(&format!( + "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" + )); } } } @@ -671,24 +670,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ex.span, format!("cannot apply unary operator `{}`", op.as_str()), ); - let missing_trait = match op { - hir::UnOp::Deref => unreachable!("check unary op `-` or `!` only"), - hir::UnOp::Not => "std::ops::Not", - hir::UnOp::Neg => "std::ops::Neg", - }; + let mut visitor = TypeParamVisitor(vec![]); visitor.visit_ty(operand_ty); - if let [ty] = &visitor.0[..] && let ty::Param(p) = *operand_ty.kind() { - suggest_constraining_param( - self.tcx, - self.body_id, - &mut err, - *ty, - operand_ty, - missing_trait, - p, - true, - ); + if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() { + let predicates = errors + .iter() + .filter_map(|error| { + error.obligation.predicate.clone().to_opt_poly_trait_pred() + }); + for pred in predicates { + self.infcx.suggest_restricting_param_bound( + &mut err, + pred, + self.body_id, + ); + } } let sp = self.tcx.sess.source_map().start_point(ex.span); @@ -973,46 +970,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool } } -fn suggest_constraining_param( - tcx: TyCtxt<'_>, - body_id: hir::HirId, - mut err: &mut Diagnostic, - lhs_ty: Ty<'_>, - rhs_ty: Ty<'_>, - missing_trait: &str, - p: ty::ParamTy, - set_output: bool, -) { - let hir = tcx.hir(); - let msg = &format!("`{lhs_ty}` might need a bound for `{missing_trait}`"); - // Try to find the def-id and details for the parameter p. We have only the index, - // so we have to find the enclosing function's def-id, then look through its declared - // generic parameters to get the declaration. - let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id }); - let generics = tcx.generics_of(def_id); - let param_def_id = generics.type_param(&p, tcx).def_id; - if let Some(generics) = param_def_id - .as_local() - .map(|id| hir.local_def_id_to_hir_id(id)) - .and_then(|id| hir.find_by_def_id(hir.get_parent_item(id))) - .as_ref() - .and_then(|node| node.generics()) - { - let output = if set_output { format!("") } else { String::new() }; - suggest_constraining_type_param( - tcx, - generics, - &mut err, - &lhs_ty.to_string(), - &format!("{missing_trait}{output}"), - None, - ); - } else { - let span = tcx.def_span(param_def_id); - err.span_label(span, msg); - } -} - struct TypeParamVisitor<'tcx>(Vec>); impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { diff --git a/src/test/ui/binop/issue-93927.rs b/src/test/ui/binop/issue-93927.rs new file mode 100644 index 0000000000000..de27c9785e65a --- /dev/null +++ b/src/test/ui/binop/issue-93927.rs @@ -0,0 +1,20 @@ +// Regression test for #93927: suggested trait bound for T should be Eq, not PartialEq +struct MyType(T); + +impl PartialEq for MyType +where + T: Eq, +{ + fn eq(&self, other: &Self) -> bool { + true + } +} + +fn cond(val: MyType) -> bool { + val == val + //~^ ERROR binary operation `==` cannot be applied to type `MyType` +} + +fn main() { + cond(MyType(0)); +} diff --git a/src/test/ui/binop/issue-93927.stderr b/src/test/ui/binop/issue-93927.stderr new file mode 100644 index 0000000000000..75558b502f9dc --- /dev/null +++ b/src/test/ui/binop/issue-93927.stderr @@ -0,0 +1,16 @@ +error[E0369]: binary operation `==` cannot be applied to type `MyType` + --> $DIR/issue-93927.rs:14:9 + | +LL | val == val + | --- ^^ --- MyType + | | + | MyType + | +help: consider further restricting this bound + | +LL | fn cond(val: MyType) -> bool { + | ++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed deleted file mode 100644 index 0e234120a51c5..0000000000000 --- a/src/test/ui/generic-associated-types/missing-bounds.fixed +++ /dev/null @@ -1,46 +0,0 @@ -// run-rustfix - -use std::ops::Add; - -struct A(B); - -impl Add for A where B: Add + Add { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - A(self.0 + rhs.0) //~ ERROR mismatched types - } -} - -struct C(B); - -impl> Add for C { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - Self(self.0 + rhs.0) //~ ERROR mismatched types - } -} - -struct D(B); - -impl> Add for D { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B` - } -} - -struct E(B); - -impl Add for E where B: Add, B: Add { - //~^ ERROR equality constraints are not yet supported in `where` clauses - type Output = Self; - - fn add(self, rhs: Self) -> Self { - Self(self.0 + rhs.0) //~ ERROR mismatched types - } -} - -fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs index ffafff5e9f586..b3661ba3744ee 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.rs +++ b/src/test/ui/generic-associated-types/missing-bounds.rs @@ -1,5 +1,3 @@ -// run-rustfix - use std::ops::Add; struct A(B); diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr index 240be93cf9617..aaeec920527ed 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.stderr +++ b/src/test/ui/generic-associated-types/missing-bounds.stderr @@ -1,5 +1,5 @@ error: equality constraints are not yet supported in `where` clauses - --> $DIR/missing-bounds.rs:37:33 + --> $DIR/missing-bounds.rs:35:33 | LL | impl Add for E where ::Output = B { | ^^^^^^^^^^^^^^^^^^^^^^ not supported @@ -11,7 +11,7 @@ LL | impl Add for E where B: Add { | ~~~~~~~~~~~~~~~~~~ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:11:11 + --> $DIR/missing-bounds.rs:9:11 | LL | impl Add for A where B: Add { | - this type parameter @@ -24,7 +24,7 @@ LL | A(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:5:8 + --> $DIR/missing-bounds.rs:3:8 | LL | struct A(B); | ^ @@ -34,7 +34,7 @@ LL | impl Add for A where B: Add + Add { | +++++++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:21:14 + --> $DIR/missing-bounds.rs:19:14 | LL | impl Add for C { | - this type parameter @@ -47,7 +47,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:15:8 + --> $DIR/missing-bounds.rs:13:8 | LL | struct C(B); | ^ @@ -57,7 +57,7 @@ LL | impl> Add for C { | +++++++++++++++++ error[E0369]: cannot add `B` to `B` - --> $DIR/missing-bounds.rs:31:21 + --> $DIR/missing-bounds.rs:29:21 | LL | Self(self.0 + rhs.0) | ------ ^ ----- B @@ -66,11 +66,11 @@ LL | Self(self.0 + rhs.0) | help: consider restricting type parameter `B` | -LL | impl> Add for D { - | +++++++++++++++++++++++++++ +LL | impl Add for D { + | +++++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:42:14 + --> $DIR/missing-bounds.rs:40:14 | LL | impl Add for E where ::Output = B { | - this type parameter @@ -83,7 +83,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:35:8 + --> $DIR/missing-bounds.rs:33:8 | LL | struct E(B); | ^ diff --git a/src/test/ui/issues/issue-35668.stderr b/src/test/ui/issues/issue-35668.stderr index 04faea9008a11..07409e9834a46 100644 --- a/src/test/ui/issues/issue-35668.stderr +++ b/src/test/ui/issues/issue-35668.stderr @@ -6,10 +6,10 @@ LL | a.iter().map(|a| a*a) | | | &T | -help: consider restricting type parameter `T` +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement | -LL | fn func<'a, T: std::ops::Mul>(a: &'a [T]) -> impl Iterator { - | ++++++++++++++++++++++++++++ +LL | fn func<'a, T>(a: &'a [T]) -> impl Iterator where &T: Mul<&T> { + | +++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/suggestions/invalid-bin-op.stderr b/src/test/ui/suggestions/invalid-bin-op.stderr index d18c24e53d030..fe5e2b5816fdf 100644 --- a/src/test/ui/suggestions/invalid-bin-op.stderr +++ b/src/test/ui/suggestions/invalid-bin-op.stderr @@ -11,11 +11,14 @@ note: an implementation of `PartialEq<_>` might be missing for `S` | LL | struct S(T); | ^^^^^^^^^^^^^^^ must implement `PartialEq<_>` - = note: the trait `std::cmp::PartialEq` is not implemented for `S` help: consider annotating `S` with `#[derive(PartialEq)]` | LL | #[derive(PartialEq)] | +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +LL | pub fn foo(s: S, t: S) where S: PartialEq { + | +++++++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/traits/resolution-in-overloaded-op.stderr b/src/test/ui/traits/resolution-in-overloaded-op.stderr index 049fffe165ab5..3ae6bf130cc7e 100644 --- a/src/test/ui/traits/resolution-in-overloaded-op.stderr +++ b/src/test/ui/traits/resolution-in-overloaded-op.stderr @@ -6,10 +6,10 @@ LL | a * b | | | &T | -help: consider further restricting this bound +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement | -LL | fn foo + std::ops::Mul>(a: &T, b: f64) -> f64 { - | +++++++++++++++++++++++++++++ +LL | fn foo>(a: &T, b: f64) -> f64 where &T: Mul { + | ++++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/type/type-check/missing_trait_impl.stderr b/src/test/ui/type/type-check/missing_trait_impl.stderr index 59b8692dd4d1a..2b58cd4180bd3 100644 --- a/src/test/ui/type/type-check/missing_trait_impl.stderr +++ b/src/test/ui/type/type-check/missing_trait_impl.stderr @@ -8,8 +8,8 @@ LL | let z = x + y; | help: consider restricting type parameter `T` | -LL | fn foo>(x: T, y: T) { - | +++++++++++++++++++++++++++ +LL | fn foo(x: T, y: T) { + | +++++++++++++++ error[E0368]: binary assignment operation `+=` cannot be applied to type `T` --> $DIR/missing_trait_impl.rs:9:5 @@ -32,8 +32,8 @@ LL | let y = -x; | help: consider restricting type parameter `T` | -LL | fn baz>(x: T) { - | +++++++++++++++++++++++++++ +LL | fn baz(x: T) { + | +++++++++++++++ error[E0600]: cannot apply unary operator `!` to type `T` --> $DIR/missing_trait_impl.rs:14:13 @@ -43,8 +43,8 @@ LL | let y = !x; | help: consider restricting type parameter `T` | -LL | fn baz>(x: T) { - | +++++++++++++++++++++++++++ +LL | fn baz(x: T) { + | +++++++++++++++ error[E0614]: type `T` cannot be dereferenced --> $DIR/missing_trait_impl.rs:15:13