Skip to content

Commit 30e3673

Browse files
Add associated item binding to non-param-ty where clause suggestions
1 parent 0dbbf0f commit 30e3673

File tree

7 files changed

+65
-56
lines changed

7 files changed

+65
-56
lines changed

compiler/rustc_middle/src/traits/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub mod util;
1212
use crate::infer::canonical::Canonical;
1313
use crate::ty::abstract_const::NotConstEvaluatable;
1414
use crate::ty::subst::SubstsRef;
15-
use crate::ty::{self, AdtKind, Predicate, Ty, TyCtxt};
15+
use crate::ty::{self, AdtKind, Ty, TyCtxt};
1616

1717
use rustc_data_structures::sync::Lrc;
1818
use rustc_errors::{Applicability, Diagnostic};
@@ -416,7 +416,7 @@ pub enum ObligationCauseCode<'tcx> {
416416
BinOp {
417417
rhs_span: Option<Span>,
418418
is_lit: bool,
419-
output_pred: Option<Predicate<'tcx>>,
419+
output_ty: Option<Ty<'tcx>>,
420420
},
421421
}
422422

compiler/rustc_middle/src/ty/diagnostics.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,25 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
102102
generics: &hir::Generics<'_>,
103103
err: &mut Diagnostic,
104104
trait_pred: PolyTraitPredicate<'tcx>,
105+
associated_ty: Option<(&'static str, Ty<'tcx>)>,
105106
) -> bool {
106107
if !trait_pred.is_suggestable(tcx, false) {
107108
return false;
108109
}
109110

110111
let param_name = trait_pred.skip_binder().self_ty().to_string();
111-
let constraint = trait_pred.print_modifiers_and_trait_path().to_string();
112+
let mut constraint = trait_pred.print_modifiers_and_trait_path().to_string();
113+
114+
if let Some((name, term)) = associated_ty {
115+
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
116+
// That should be extracted into a helper function.
117+
if constraint.ends_with('>') {
118+
constraint = format!("{}, {}={}>", &constraint[..constraint.len() - 1], name, term);
119+
} else {
120+
constraint.push_str(&format!("<{}={}>", name, term));
121+
}
122+
}
123+
112124
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
113125

114126
// Skip, there is a param named Self

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+13-11
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ use rustc_middle::hir::map;
2525
use rustc_middle::ty::{
2626
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
2727
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable,
28-
ProjectionPredicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
29-
TypeVisitable,
28+
ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
3029
};
3130
use rustc_middle::ty::{TypeAndMut, TypeckResults};
3231
use rustc_session::Limit;
@@ -174,7 +173,7 @@ pub trait InferCtxtExt<'tcx> {
174173
&self,
175174
err: &mut Diagnostic,
176175
trait_pred: ty::PolyTraitPredicate<'tcx>,
177-
proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>,
176+
associated_item: Option<(&'static str, Ty<'tcx>)>,
178177
body_id: hir::HirId,
179178
);
180179

@@ -467,7 +466,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
467466
&self,
468467
mut err: &mut Diagnostic,
469468
trait_pred: ty::PolyTraitPredicate<'tcx>,
470-
proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>,
469+
associated_ty: Option<(&'static str, Ty<'tcx>)>,
471470
body_id: hir::HirId,
472471
) {
473472
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
@@ -604,21 +603,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
604603
trait_pred.print_modifiers_and_trait_path().to_string()
605604
);
606605

607-
if let Some(proj_pred) = proj_pred {
608-
let ProjectionPredicate { projection_ty, term } = proj_pred.skip_binder();
609-
let item = self.tcx.associated_item(projection_ty.item_def_id);
610-
606+
if let Some((name, term)) = associated_ty {
611607
// FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.
612608
// That should be extracted into a helper function.
613609
if constraint.ends_with('>') {
614610
constraint = format!(
615611
"{}, {}={}>",
616612
&constraint[..constraint.len() - 1],
617-
item.name,
613+
name,
618614
term
619615
);
620616
} else {
621-
constraint.push_str(&format!("<{}={}>", item.name, term));
617+
constraint.push_str(&format!("<{}={}>", name, term));
622618
}
623619
}
624620

@@ -648,7 +644,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
648644
..
649645
}) if !param_ty => {
650646
// Missing generic type parameter bound.
651-
if suggest_arbitrary_trait_bound(self.tcx, generics, &mut err, trait_pred) {
647+
if suggest_arbitrary_trait_bound(
648+
self.tcx,
649+
generics,
650+
&mut err,
651+
trait_pred,
652+
associated_ty,
653+
) {
652654
return;
653655
}
654656
}

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
409409
rhs_span: opt_input_expr.map(|expr| expr.span),
410410
is_lit: opt_input_expr
411411
.map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
412-
output_pred: None,
412+
output_ty: None,
413413
},
414414
),
415415
self.param_env,

compiler/rustc_typeck/src/check/method/mod.rs

+4-22
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ use rustc_hir::def_id::DefId;
2020
use rustc_infer::infer::{self, InferOk};
2121
use rustc_middle::ty::subst::Subst;
2222
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
23-
use rustc_middle::ty::{
24-
self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy,
25-
ToPredicate, Ty, TypeVisitable,
26-
};
23+
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable};
2724
use rustc_span::symbol::Ident;
2825
use rustc_span::Span;
2926
use rustc_trait_selection::traits;
@@ -337,22 +334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
337334

338335
// Construct an obligation
339336
let poly_trait_ref = ty::Binder::dummy(trait_ref);
340-
let opt_output_ty =
341-
expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
342-
let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind(
343-
self.tcx,
344-
Ident::from_str("Output"),
345-
AssocKind::Type,
346-
trait_def_id,
347-
);
348-
let output_pred =
349-
opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| {
350-
ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate {
351-
projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id },
352-
term: output_ty.into(),
353-
}))
354-
.to_predicate(self.tcx)
355-
});
337+
let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
356338

357339
(
358340
traits::Obligation::new(
@@ -363,7 +345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
363345
rhs_span: opt_input_expr.map(|expr| expr.span),
364346
is_lit: opt_input_expr
365347
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
366-
output_pred,
348+
output_ty,
367349
},
368350
),
369351
self.param_env,
@@ -518,7 +500,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
518500
rhs_span: opt_input_expr.map(|expr| expr.span),
519501
is_lit: opt_input_expr
520502
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
521-
output_pred: None,
503+
output_ty: None,
522504
},
523505
)
524506
} else {

compiler/rustc_typeck/src/check/op.rs

+30-17
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::ty::adjustment::{
1212
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
1313
};
1414
use rustc_middle::ty::print::with_no_trimmed_paths;
15-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
15+
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
1616
use rustc_span::source_map::Spanned;
1717
use rustc_span::symbol::{sym, Ident};
1818
use rustc_span::Span;
@@ -310,10 +310,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
310310
// error types are considered "builtin"
311311
Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
312312
Err(errors) => {
313-
let (_, item) = lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
314-
let missing_trait =
315-
item.map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
316-
let (mut err, use_output) = match is_assign {
313+
let (_, trait_def_id) =
314+
lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
315+
let missing_trait = trait_def_id
316+
.map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
317+
let (mut err, output_def_id) = match is_assign {
317318
IsAssign::Yes => {
318319
let mut err = struct_span_err!(
319320
self.tcx.sess,
@@ -328,7 +329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
328329
format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
329330
);
330331
self.note_unmet_impls_on_type(&mut err, errors);
331-
(err, false)
332+
(err, None)
332333
}
333334
IsAssign::No => {
334335
let message = match op.node {
@@ -368,19 +369,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
368369
lhs_ty
369370
),
370371
};
371-
let use_output = item.map_or(false, |def_id| {
372-
self.tcx.associated_item_def_ids(def_id).iter().any(|item_def_id| {
373-
self.tcx.opt_associated_item(*item_def_id).unwrap().name
374-
== sym::Output
375-
})
372+
let output_def_id = trait_def_id.and_then(|def_id| {
373+
self.tcx
374+
.associated_item_def_ids(def_id)
375+
.iter()
376+
.find(|item_def_id| {
377+
self.tcx.associated_item(*item_def_id).name == sym::Output
378+
})
379+
.cloned()
376380
});
377381
let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
378382
if !lhs_expr.span.eq(&rhs_expr.span) {
379383
err.span_label(lhs_expr.span, lhs_ty.to_string());
380384
err.span_label(rhs_expr.span, rhs_ty.to_string());
381385
}
382386
self.note_unmet_impls_on_type(&mut err, errors);
383-
(err, use_output)
387+
(err, output_def_id)
384388
}
385389
};
386390

@@ -488,20 +492,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
488492
if let Some(trait_pred) =
489493
error.obligation.predicate.to_opt_poly_trait_pred()
490494
{
491-
let proj_pred = match error.obligation.cause.code() {
495+
let output_associated_item = match error.obligation.cause.code()
496+
{
492497
ObligationCauseCode::BinOp {
493-
output_pred: Some(output_pred),
498+
output_ty: Some(output_ty),
494499
..
495-
} if use_output => {
496-
output_pred.to_opt_poly_projection_pred()
500+
} => {
501+
// Make sure that we're attaching `Output = ..` to the right trait predicate
502+
if let Some(output_def_id) = output_def_id
503+
&& let Some(trait_def_id) = trait_def_id
504+
&& self.tcx.parent(output_def_id) == trait_def_id
505+
{
506+
Some(("Output", *output_ty))
507+
} else {
508+
None
509+
}
497510
}
498511
_ => None,
499512
};
500513

501514
self.suggest_restricting_param_bound(
502515
&mut err,
503516
trait_pred,
504-
proj_pred,
517+
output_associated_item,
505518
self.body_id,
506519
);
507520
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ LL | a * b
88
|
99
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
1010
|
11-
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
12-
| ++++++++++++++++++
11+
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64, Output=f64> {
12+
| ++++++++++++++++++++++++++++++
1313

1414
error: aborting due to previous error
1515

0 commit comments

Comments
 (0)