Skip to content

Commit e8fc076

Browse files
committed
Consider unfulfilled obligations in binop errors
When encountering a binop where the types would have been accepted, if all the predicates had been fulfilled, include information about the predicates and suggest appropriate `#[derive]`s if possible. Point at trait(s) that needs to be `impl`emented.
1 parent 074f636 commit e8fc076

27 files changed

+667
-108
lines changed

compiler/rustc_middle/src/ty/print/pretty.rs

+21
Original file line numberDiff line numberDiff line change
@@ -2171,10 +2171,26 @@ impl fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> {
21712171
}
21722172
}
21732173

2174+
/// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only
2175+
/// the trait name. That is, it will print `Trait` instead of
2176+
/// `<T as Trait<U>>`.
2177+
#[derive(Copy, Clone, TypeFoldable, Lift)]
2178+
pub struct TraitRefPrintOnlyTraitName<'tcx>(ty::TraitRef<'tcx>);
2179+
2180+
impl fmt::Debug for TraitRefPrintOnlyTraitName<'tcx> {
2181+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2182+
fmt::Display::fmt(self, f)
2183+
}
2184+
}
2185+
21742186
impl ty::TraitRef<'tcx> {
21752187
pub fn print_only_trait_path(self) -> TraitRefPrintOnlyTraitPath<'tcx> {
21762188
TraitRefPrintOnlyTraitPath(self)
21772189
}
2190+
2191+
pub fn print_only_trait_name(self) -> TraitRefPrintOnlyTraitName<'tcx> {
2192+
TraitRefPrintOnlyTraitName(self)
2193+
}
21782194
}
21792195

21802196
impl ty::Binder<'tcx, ty::TraitRef<'tcx>> {
@@ -2193,6 +2209,7 @@ forward_display_to_print! {
21932209
ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>,
21942210
ty::Binder<'tcx, ty::TraitRef<'tcx>>,
21952211
ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
2212+
ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>,
21962213
ty::Binder<'tcx, ty::FnSig<'tcx>>,
21972214
ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
21982215
ty::Binder<'tcx, ty::SubtypePredicate<'tcx>>,
@@ -2255,6 +2272,10 @@ define_print_and_forward_display! {
22552272
p!(print_def_path(self.0.def_id, self.0.substs));
22562273
}
22572274

2275+
TraitRefPrintOnlyTraitName<'tcx> {
2276+
p!(print_def_path(self.0.def_id, &[]));
2277+
}
2278+
22582279
ty::ParamTy {
22592280
p!(write("{}", self.name))
22602281
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
521521
"this error might have been caused by changes to \
522522
Rust's type-inference algorithm (see issue #48950 \
523523
<https://github.com/rust-lang/rust/issues/48950> \
524-
for more information).",
524+
for more information)",
525525
);
526526
err.help("did you intend to use the type `()` here instead?");
527527
}

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

+41-25
Original file line numberDiff line numberDiff line change
@@ -290,29 +290,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
290290
)
291291
}
292292

293-
/// `lookup_method_in_trait` is used for overloaded operators.
294-
/// It does a very narrow slice of what the normal probe/confirm path does.
295-
/// In particular, it doesn't really do any probing: it simply constructs
296-
/// an obligation for a particular trait with the given self type and checks
297-
/// whether that trait is implemented.
298-
//
299-
// FIXME(#18741): it seems likely that we can consolidate some of this
300-
// code with the other method-lookup code. In particular, the second half
301-
// of this method is basically the same as confirmation.
302-
#[instrument(level = "debug", skip(self, span, opt_input_types))]
303-
pub fn lookup_method_in_trait(
293+
pub(super) fn obligation_for_method(
304294
&self,
305295
span: Span,
306-
m_name: Ident,
307296
trait_def_id: DefId,
308297
self_ty: Ty<'tcx>,
309298
opt_input_types: Option<&[Ty<'tcx>]>,
310-
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
311-
debug!(
312-
"lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
313-
self_ty, m_name, trait_def_id, opt_input_types
314-
);
315-
299+
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
300+
{
316301
// Construct a trait-reference `self_ty : Trait<input_tys>`
317302
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
318303
match param.kind {
@@ -332,17 +317,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
332317

333318
// Construct an obligation
334319
let poly_trait_ref = ty::Binder::dummy(trait_ref);
335-
let obligation = traits::Obligation::misc(
336-
span,
337-
self.body_id,
338-
self.param_env,
339-
poly_trait_ref.without_const().to_predicate(self.tcx),
320+
(
321+
traits::Obligation::misc(
322+
span,
323+
self.body_id,
324+
self.param_env,
325+
poly_trait_ref.without_const().to_predicate(self.tcx),
326+
),
327+
substs,
328+
)
329+
}
330+
331+
/// `lookup_method_in_trait` is used for overloaded operators.
332+
/// It does a very narrow slice of what the normal probe/confirm path does.
333+
/// In particular, it doesn't really do any probing: it simply constructs
334+
/// an obligation for a particular trait with the given self type and checks
335+
/// whether that trait is implemented.
336+
//
337+
// FIXME(#18741): it seems likely that we can consolidate some of this
338+
// code with the other method-lookup code. In particular, the second half
339+
// of this method is basically the same as confirmation.
340+
#[instrument(level = "debug", skip(self, span, opt_input_types))]
341+
pub(super) fn lookup_method_in_trait(
342+
&self,
343+
span: Span,
344+
m_name: Ident,
345+
trait_def_id: DefId,
346+
self_ty: Ty<'tcx>,
347+
opt_input_types: Option<&[Ty<'tcx>]>,
348+
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
349+
debug!(
350+
"lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
351+
self_ty, m_name, trait_def_id, opt_input_types
340352
);
341353

354+
let (obligation, substs) =
355+
self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
356+
342357
// Now we want to know if this can be matched
343358
if !self.predicate_may_hold(&obligation) {
344359
debug!("--> Cannot match obligation");
345-
return None; // Cannot be matched, no such method resolution is possible.
360+
// Cannot be matched, no such method resolution is possible.
361+
return None;
346362
}
347363

348364
// Trait must have a method named `m_name` and it should not have
@@ -416,7 +432,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
416432
ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx),
417433
));
418434

419-
let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig };
435+
let callee = MethodCallee { def_id, substs, sig: fn_sig };
420436

421437
debug!("callee = {:?}", callee);
422438

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

+118-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ use rustc_middle::ty::print::with_crate_prefix;
1515
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
1616
use rustc_span::lev_distance;
1717
use rustc_span::symbol::{kw, sym, Ident};
18-
use rustc_span::{source_map, FileName, Span};
18+
use rustc_span::{source_map, FileName, MultiSpan, Span};
1919
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
20-
use rustc_trait_selection::traits::Obligation;
20+
use rustc_trait_selection::traits::{FulfillmentError, Obligation};
2121

2222
use std::cmp::Ordering;
2323
use std::iter;
@@ -969,6 +969,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
969969
None
970970
}
971971

972+
crate fn note_unmet_impls_on_type(
973+
&self,
974+
err: &mut rustc_errors::DiagnosticBuilder<'_>,
975+
errors: Vec<FulfillmentError<'tcx>>,
976+
) {
977+
let all_local_types_needing_impls =
978+
errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
979+
ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() {
980+
ty::Adt(def, _) => def.did.is_local(),
981+
_ => false,
982+
},
983+
_ => false,
984+
});
985+
let mut preds: Vec<_> = errors
986+
.iter()
987+
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
988+
ty::PredicateKind::Trait(pred) => Some(pred),
989+
_ => None,
990+
})
991+
.collect();
992+
preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty()));
993+
let def_ids = preds
994+
.iter()
995+
.filter_map(|pred| match pred.self_ty().kind() {
996+
ty::Adt(def, _) => Some(def.did),
997+
_ => None,
998+
})
999+
.collect::<FxHashSet<_>>();
1000+
let sm = self.tcx.sess.source_map();
1001+
let mut spans: MultiSpan = def_ids
1002+
.iter()
1003+
.filter_map(|def_id| {
1004+
let span = self.tcx.def_span(*def_id);
1005+
if span.is_dummy() { None } else { Some(sm.guess_head_span(span)) }
1006+
})
1007+
.collect::<Vec<_>>()
1008+
.into();
1009+
1010+
for pred in &preds {
1011+
match pred.self_ty().kind() {
1012+
ty::Adt(def, _) => {
1013+
spans.push_span_label(
1014+
sm.guess_head_span(self.tcx.def_span(def.did)),
1015+
format!("must implement `{}`", pred.trait_ref.print_only_trait_path()),
1016+
);
1017+
}
1018+
_ => {}
1019+
}
1020+
}
1021+
1022+
if all_local_types_needing_impls && spans.primary_span().is_some() {
1023+
let msg = if preds.len() == 1 {
1024+
format!(
1025+
"an implementation of `{}` might be missing for `{}`",
1026+
preds[0].trait_ref.print_only_trait_path(),
1027+
preds[0].self_ty()
1028+
)
1029+
} else {
1030+
format!(
1031+
"the following type{} would have to `impl` {} required trait{} for this \
1032+
operation to be valid",
1033+
pluralize!(def_ids.len()),
1034+
if def_ids.len() == 1 { "its" } else { "their" },
1035+
pluralize!(preds.len()),
1036+
)
1037+
};
1038+
err.span_note(spans, &msg);
1039+
}
1040+
1041+
let preds: Vec<_> = errors.iter().map(|e| (e.obligation.predicate, None)).collect();
1042+
self.suggest_derive(err, &preds);
1043+
}
1044+
9721045
fn suggest_derive(
9731046
&self,
9741047
err: &mut DiagnosticBuilder<'_>,
@@ -1010,7 +1083,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10101083
return Some((
10111084
format!("{}", trait_ref.self_ty()),
10121085
self.tcx.def_span(adt_def.did),
1013-
format!("{}", trait_ref.print_only_trait_path()),
1086+
format!("{}", trait_ref.print_only_trait_name()),
10141087
));
10151088
}
10161089
None
@@ -1033,6 +1106,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10331106
acc
10341107
},
10351108
);
1109+
let mut traits: Vec<_> = unsatisfied_predicates
1110+
.iter()
1111+
.filter_map(|(pred, _)| {
1112+
let trait_pred =
1113+
if let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() {
1114+
trait_pred
1115+
} else {
1116+
return None;
1117+
};
1118+
if let ty::Adt(adt_def, _) = trait_pred.trait_ref.self_ty().kind() {
1119+
if !adt_def.did.is_local() {
1120+
return None;
1121+
}
1122+
} else {
1123+
return None;
1124+
};
1125+
1126+
let did = trait_pred.def_id();
1127+
let diagnostic_items = self.tcx.diagnostic_items(did.krate);
1128+
1129+
if !derivables
1130+
.iter()
1131+
.any(|trait_derivable| diagnostic_items.get(trait_derivable) == Some(&did))
1132+
{
1133+
Some(self.tcx.def_span(did))
1134+
} else {
1135+
None
1136+
}
1137+
})
1138+
.collect();
1139+
traits.sort();
1140+
traits.dedup();
1141+
1142+
let len = traits.len();
1143+
if len > 0 {
1144+
let span: MultiSpan = traits.into();
1145+
err.span_note(
1146+
span,
1147+
&format!("the following trait{} must be implemented", pluralize!(len),),
1148+
);
1149+
}
1150+
10361151
for (self_name, self_span, traits) in &derives_grouped {
10371152
err.span_suggestion_verbose(
10381153
self_span.shrink_to_lo(),

0 commit comments

Comments
 (0)