Skip to content

Commit 4d0fe27

Browse files
committed
Replace suggest_constraining_param with suggest_restricting_param_bound
to fix incorrect suggestion for trait bounds involving binary operators. Fixes #93927, #92347, #93744.
1 parent 18b53ce commit 4d0fe27

File tree

9 files changed

+80
-89
lines changed

9 files changed

+80
-89
lines changed

compiler/rustc_typeck/src/check/op.rs

+29-77
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ use rustc_middle::ty::adjustment::{
1111
};
1212
use rustc_middle::ty::fold::TypeFolder;
1313
use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
14-
use rustc_middle::ty::{
15-
self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor,
16-
};
14+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor};
1715
use rustc_span::source_map::Spanned;
1816
use rustc_span::symbol::{sym, Ident};
1917
use rustc_span::Span;
2018
use rustc_trait_selection::infer::InferCtxtExt;
19+
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _;
2120
use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
2221

2322
use std::ops::ControlFlow;
@@ -266,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
266265
Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
267266
Err(errors) => {
268267
let source_map = self.tcx.sess.source_map();
269-
let (mut err, missing_trait, use_output) = match is_assign {
268+
let (mut err, missing_trait, _use_output) = match is_assign {
270269
IsAssign::Yes => {
271270
let mut err = struct_span_err!(
272271
self.tcx.sess,
@@ -449,40 +448,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
449448
// concatenation (e.g., "Hello " + "World!"). This means
450449
// we don't want the note in the else clause to be emitted
451450
} else if let [ty] = &visitor.0[..] {
452-
if let ty::Param(p) = *ty.kind() {
453-
// Check if the method would be found if the type param wasn't
454-
// involved. If so, it means that adding a trait bound to the param is
455-
// enough. Otherwise we do not give the suggestion.
456-
let mut eraser = TypeParamEraser(self, expr.span);
457-
let needs_bound = self
458-
.lookup_op_method(
459-
eraser.fold_ty(lhs_ty),
460-
Some(eraser.fold_ty(rhs_ty)),
461-
Some(rhs_expr),
462-
Op::Binary(op, is_assign),
463-
)
464-
.is_ok();
465-
if needs_bound {
466-
suggest_constraining_param(
467-
self.tcx,
468-
self.body_id,
469-
&mut err,
470-
*ty,
471-
rhs_ty,
472-
missing_trait,
473-
p,
474-
use_output,
475-
);
476-
} else if *ty != lhs_ty {
477-
// When we know that a missing bound is responsible, we don't show
478-
// this note as it is redundant.
479-
err.note(&format!(
480-
"the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
481-
));
482-
}
483-
} else {
484-
bug!("type param visitor stored a non type param: {:?}", ty.kind());
451+
// Look for a TraitPredicate in the Fulfillment errors,
452+
// and use it to generate a suggestion.
453+
//
454+
// Note that lookup_op_method must be called again but
455+
// with a specific rhs_ty instead of a placeholder so
456+
// the resulting predicate generates a more specific
457+
// suggestion for the user.
458+
let errors = self
459+
.lookup_op_method(lhs_ty, &[rhs_ty], Op::Binary(op, is_assign))
460+
.unwrap_err();
461+
let predicates = errors
462+
.into_iter()
463+
.filter_map(|error| error.obligation.predicate.to_opt_poly_trait_pred())
464+
.collect::<Vec<_>>();
465+
if !predicates.is_empty() {
466+
for pred in predicates {
467+
self.infcx.suggest_restricting_param_bound(&mut err,
468+
pred,
469+
self.body_id,
470+
);
485471
}
472+
} else if *ty != lhs_ty {
473+
// When we know that a missing bound is responsible, we don't show
474+
// this note as it is redundant.
475+
err.note(&format!(
476+
"the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
477+
));
486478
}
487479
}
488480
err.emit();
@@ -973,46 +965,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
973965
}
974966
}
975967

976-
fn suggest_constraining_param(
977-
tcx: TyCtxt<'_>,
978-
body_id: hir::HirId,
979-
mut err: &mut Diagnostic,
980-
lhs_ty: Ty<'_>,
981-
rhs_ty: Ty<'_>,
982-
missing_trait: &str,
983-
p: ty::ParamTy,
984-
set_output: bool,
985-
) {
986-
let hir = tcx.hir();
987-
let msg = &format!("`{lhs_ty}` might need a bound for `{missing_trait}`");
988-
// Try to find the def-id and details for the parameter p. We have only the index,
989-
// so we have to find the enclosing function's def-id, then look through its declared
990-
// generic parameters to get the declaration.
991-
let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
992-
let generics = tcx.generics_of(def_id);
993-
let param_def_id = generics.type_param(&p, tcx).def_id;
994-
if let Some(generics) = param_def_id
995-
.as_local()
996-
.map(|id| hir.local_def_id_to_hir_id(id))
997-
.and_then(|id| hir.find_by_def_id(hir.get_parent_item(id)))
998-
.as_ref()
999-
.and_then(|node| node.generics())
1000-
{
1001-
let output = if set_output { format!("<Output = {rhs_ty}>") } else { String::new() };
1002-
suggest_constraining_type_param(
1003-
tcx,
1004-
generics,
1005-
&mut err,
1006-
&lhs_ty.to_string(),
1007-
&format!("{missing_trait}{output}"),
1008-
None,
1009-
);
1010-
} else {
1011-
let span = tcx.def_span(param_def_id);
1012-
err.span_label(span, msg);
1013-
}
1014-
}
1015-
1016968
struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
1017969

1018970
impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {

src/test/ui/binop/issue-93927.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Regression test for #93927: suggested trait bound for T should be Eq, not PartialEq
2+
struct MyType<T>(T);
3+
4+
impl<T> PartialEq for MyType<T>
5+
where
6+
T: Eq,
7+
{
8+
fn eq(&self, other: &Self) -> bool {
9+
true
10+
}
11+
}
12+
13+
fn cond<T: PartialEq>(val: MyType<T>) -> bool {
14+
val == val
15+
//~^ ERROR binary operation `==` cannot be applied to type `MyType<T>`
16+
}
17+
18+
fn main() {
19+
cond(MyType(0));
20+
}

src/test/ui/binop/issue-93927.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0369]: binary operation `==` cannot be applied to type `MyType<T>`
2+
--> $DIR/issue-93927.rs:14:9
3+
|
4+
LL | val == val
5+
| --- ^^ --- MyType<T>
6+
| |
7+
| MyType<T>
8+
|
9+
help: consider further restricting this bound
10+
|
11+
LL | fn cond<T: PartialEq + std::cmp::Eq>(val: MyType<T>) -> bool {
12+
| ++++++++++++++
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0369`.

src/test/ui/generic-associated-types/missing-bounds.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl<B: Add + Add<Output = B>> Add for C<B> {
2424

2525
struct D<B>(B);
2626

27-
impl<B: std::ops::Add<Output = B>> Add for D<B> {
27+
impl<B: std::ops::Add> Add for D<B> {
2828
type Output = Self;
2929

3030
fn add(self, rhs: Self) -> Self {

src/test/ui/generic-associated-types/missing-bounds.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ LL | Self(self.0 + rhs.0)
6666
|
6767
help: consider restricting type parameter `B`
6868
|
69-
LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
70-
| +++++++++++++++++++++++++++
69+
LL | impl<B: std::ops::Add> Add for D<B> {
70+
| +++++++++++++++
7171

7272
error[E0308]: mismatched types
7373
--> $DIR/missing-bounds.rs:42:14

src/test/ui/issues/issue-35668.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ LL | a.iter().map(|a| a*a)
66
| |
77
| &T
88
|
9-
help: consider restricting type parameter `T`
9+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
1010
|
11-
LL | fn func<'a, T: std::ops::Mul<Output = &T>>(a: &'a [T]) -> impl Iterator<Item=&'a T> {
12-
| ++++++++++++++++++++++++++++
11+
LL | fn func<'a, T>(a: &'a [T]) -> impl Iterator<Item=&'a T> where &T: Mul<&T> {
12+
| +++++++++++++++++
1313

1414
error: aborting due to previous error
1515

src/test/ui/suggestions/invalid-bin-op.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ note: an implementation of `PartialEq<_>` might be missing for `S<T>`
1111
|
1212
LL | struct S<T>(T);
1313
| ^^^^^^^^^^^^^^^ must implement `PartialEq<_>`
14-
= note: the trait `std::cmp::PartialEq` is not implemented for `S<T>`
1514
help: consider annotating `S<T>` with `#[derive(PartialEq)]`
1615
|
1716
LL | #[derive(PartialEq)]
1817
|
18+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
19+
|
20+
LL | pub fn foo<T>(s: S<T>, t: S<T>) where S<T>: PartialEq {
21+
| +++++++++++++++++++++
1922

2023
error: aborting due to previous error
2124

src/test/ui/traits/resolution-in-overloaded-op.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ LL | a * b
66
| |
77
| &T
88
|
9-
help: consider further restricting this bound
9+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
1010
|
11-
LL | fn foo<T: MyMul<f64, f64> + std::ops::Mul<Output = f64>>(a: &T, b: f64) -> f64 {
12-
| +++++++++++++++++++++++++++++
11+
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
12+
| ++++++++++++++++++
1313

1414
error: aborting due to previous error
1515

src/test/ui/type/type-check/missing_trait_impl.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ LL | let z = x + y;
88
|
99
help: consider restricting type parameter `T`
1010
|
11-
LL | fn foo<T: std::ops::Add<Output = T>>(x: T, y: T) {
12-
| +++++++++++++++++++++++++++
11+
LL | fn foo<T: std::ops::Add>(x: T, y: T) {
12+
| +++++++++++++++
1313

1414
error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
1515
--> $DIR/missing_trait_impl.rs:9:5

0 commit comments

Comments
 (0)