Skip to content

Commit b8e5ab2

Browse files
committed
Auto merge of #89580 - estebank:trait-bounds-are-tricky, r=nagisa
Point at source of trait bound obligations in more places Be more thorough in using `ItemObligation` and `BindingObligation` when evaluating obligations so that we can point at trait bounds that introduced unfulfilled obligations. We no longer incorrectly point at unrelated trait bounds (`substs-ppaux.verbose.stderr`). In particular, we now point at trait bounds on method calls. We no longer point at "obvious" obligation sources (we no longer have a note pointing at `Trait` saying "required by a bound in `Trait`", like in `associated-types-no-suitable-supertrait*`). We no longer point at associated items (`ImplObligation`), as they didn't add any user actionable information, they just added noise. Address part of #89418.
2 parents 02913c0 + 2a2621d commit b8e5ab2

File tree

221 files changed

+2064
-2739
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

221 files changed

+2064
-2739
lines changed

compiler/rustc_errors/src/emitter.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -1266,22 +1266,37 @@ impl EmitterWriter {
12661266
}
12671267
self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None);
12681268
} else {
1269+
let mut label_width = 0;
12691270
// The failure note level itself does not provide any useful diagnostic information
12701271
if *level != Level::FailureNote {
12711272
buffer.append(0, level.to_str(), Style::Level(*level));
1273+
label_width += level.to_str().len();
12721274
}
12731275
// only render error codes, not lint codes
12741276
if let Some(DiagnosticId::Error(ref code)) = *code {
12751277
buffer.append(0, "[", Style::Level(*level));
12761278
buffer.append(0, &code, Style::Level(*level));
12771279
buffer.append(0, "]", Style::Level(*level));
1280+
label_width += 2 + code.len();
12781281
}
12791282
let header_style = if is_secondary { Style::HeaderMsg } else { Style::MainHeaderMsg };
12801283
if *level != Level::FailureNote {
12811284
buffer.append(0, ": ", header_style);
1285+
label_width += 2;
12821286
}
12831287
for &(ref text, _) in msg.iter() {
1284-
buffer.append(0, &replace_tabs(text), header_style);
1288+
// Account for newlines to align output to its label.
1289+
for (line, text) in replace_tabs(text).lines().enumerate() {
1290+
buffer.append(
1291+
0 + line,
1292+
&format!(
1293+
"{}{}",
1294+
if line == 0 { String::new() } else { " ".repeat(label_width) },
1295+
text
1296+
),
1297+
header_style,
1298+
);
1299+
}
12851300
}
12861301
}
12871302

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -2141,10 +2141,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
21412141
None
21422142
},
21432143
self.tcx.generics_of(owner.to_def_id()),
2144+
hir.span(hir_id),
21442145
)
21452146
});
2147+
2148+
let span = match generics {
2149+
// This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal
2150+
// for other diagnostics, so we need to recover it here.
2151+
Some((_, _, node)) if span.is_dummy() => node,
2152+
_ => span,
2153+
};
2154+
21462155
let type_param_span = match (generics, bound_kind) {
2147-
(Some((_, ref generics)), GenericKind::Param(ref param)) => {
2156+
(Some((_, ref generics, _)), GenericKind::Param(ref param)) => {
21482157
// Account for the case where `param` corresponds to `Self`,
21492158
// which doesn't have the expected type argument.
21502159
if !(generics.has_self && param.index == 0) {
@@ -2181,7 +2190,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
21812190
};
21822191
let new_lt = generics
21832192
.as_ref()
2184-
.and_then(|(parent_g, g)| {
2193+
.and_then(|(parent_g, g, _)| {
21852194
let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char));
21862195
let mut lts_names = g
21872196
.params
@@ -2203,7 +2212,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
22032212
.unwrap_or("'lt".to_string());
22042213
let add_lt_sugg = generics
22052214
.as_ref()
2206-
.and_then(|(_, g)| g.params.first())
2215+
.and_then(|(_, g, _)| g.params.first())
22072216
.and_then(|param| param.def_id.as_local())
22082217
.map(|def_id| {
22092218
(

compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,16 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
192192
ObligationCauseCode::MatchImpl(parent, ..) => &parent.code,
193193
_ => &cause.code,
194194
};
195-
if let ObligationCauseCode::ItemObligation(item_def_id) = *code {
195+
if let (ObligationCauseCode::ItemObligation(item_def_id), None) =
196+
(code, override_error_code)
197+
{
196198
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
197199
// lifetime as above, but called using a fully-qualified path to the method:
198200
// `Foo::qux(bar)`.
199201
let mut v = TraitObjectVisitor(FxHashSet::default());
200202
v.visit_ty(param.param_ty);
201203
if let Some((ident, self_ty)) =
202-
self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0)
204+
self.get_impl_ident_and_self_ty_from_trait(*item_def_id, &v.0)
203205
{
204206
if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) {
205207
override_error_code = Some(ident);

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

+3-9
Original file line numberDiff line numberDiff line change
@@ -1958,15 +1958,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
19581958
region, object_ty,
19591959
));
19601960
}
1961-
ObligationCauseCode::ItemObligation(item_def_id) => {
1962-
let item_name = tcx.def_path_str(item_def_id);
1963-
let msg = format!("required by `{}`", item_name);
1964-
let sp = tcx
1965-
.hir()
1966-
.span_if_local(item_def_id)
1967-
.unwrap_or_else(|| tcx.def_span(item_def_id));
1968-
let sp = tcx.sess.source_map().guess_head_span(sp);
1969-
err.span_note(sp, &msg);
1961+
ObligationCauseCode::ItemObligation(_item_def_id) => {
1962+
// We hold the `DefId` of the item introducing the obligation, but displaying it
1963+
// doesn't add user usable information. It always point at an associated item.
19701964
}
19711965
ObligationCauseCode::BindingObligation(item_def_id, span) => {
19721966
let item_name = tcx.def_path_str(item_def_id);

compiler/rustc_trait_selection/src/traits/util.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
99
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
1010

1111
use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
12-
pub use rustc_infer::traits::util::*;
12+
pub use rustc_infer::traits::{self, util::*};
13+
14+
use std::iter;
1315

1416
///////////////////////////////////////////////////////////////////////////
1517
// `TraitAliasExpander` iterator
@@ -229,11 +231,16 @@ pub fn predicates_for_generics<'tcx>(
229231
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
230232
debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds);
231233

232-
generic_bounds.predicates.into_iter().map(move |predicate| Obligation {
233-
cause: cause.clone(),
234-
recursion_depth,
235-
param_env,
236-
predicate,
234+
iter::zip(generic_bounds.predicates, generic_bounds.spans).map(move |(predicate, span)| {
235+
let cause = match cause.code {
236+
traits::ItemObligation(def_id) if !span.is_dummy() => traits::ObligationCause::new(
237+
cause.span,
238+
cause.body_id,
239+
traits::BindingObligation(def_id, span),
240+
),
241+
_ => cause.clone(),
242+
};
243+
Obligation { cause, recursion_depth, param_env, predicate }
237244
})
238245
}
239246

compiler/rustc_trait_selection/src/traits/wf.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,12 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
709709

710710
iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev())
711711
.map(|((pred, span), origin_def_id)| {
712-
let cause = self.cause(traits::BindingObligation(origin_def_id, span));
712+
let code = if span.is_dummy() {
713+
traits::MiscObligation
714+
} else {
715+
traits::BindingObligation(origin_def_id, span)
716+
};
717+
let cause = self.cause(code);
713718
traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred)
714719
})
715720
.filter(|pred| !pred.has_escaping_bound_vars())

compiler/rustc_typeck/src/check/compare_method.rs

+21-15
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,8 @@ fn compare_predicate_entailment<'tcx>(
210210
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id);
211211
let param_env =
212212
ty::ParamEnv::new(tcx.intern_predicates(&hybrid_preds.predicates), Reveal::UserFacing);
213-
let param_env = traits::normalize_param_env_or_error(
214-
tcx,
215-
impl_m.def_id,
216-
param_env,
217-
normalize_cause.clone(),
218-
);
213+
let param_env =
214+
traits::normalize_param_env_or_error(tcx, impl_m.def_id, param_env, normalize_cause);
219215

220216
tcx.infer_ctxt().enter(|infcx| {
221217
let inh = Inherited::new(infcx, impl_m.def_id.expect_local());
@@ -226,12 +222,15 @@ fn compare_predicate_entailment<'tcx>(
226222
let mut selcx = traits::SelectionContext::new(&infcx);
227223

228224
let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
229-
for predicate in impl_m_own_bounds.predicates {
225+
for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
226+
let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
230227
let traits::Normalized { value: predicate, obligations } =
231-
traits::normalize(&mut selcx, param_env, normalize_cause.clone(), predicate);
228+
traits::normalize(&mut selcx, param_env, normalize_cause, predicate);
232229

233230
inh.register_predicates(obligations);
234-
inh.register_predicate(traits::Obligation::new(cause.clone(), param_env, predicate));
231+
let mut cause = cause.clone();
232+
cause.make_mut().span = span;
233+
inh.register_predicate(traits::Obligation::new(cause, param_env, predicate));
235234
}
236235

237236
// We now need to check that the signature of the impl method is
@@ -280,6 +279,12 @@ fn compare_predicate_entailment<'tcx>(
280279

281280
let sub_result = infcx.at(&cause, param_env).sup(trait_fty, impl_fty).map(
282281
|InferOk { obligations, .. }| {
282+
// FIXME: We'd want to keep more accurate spans than "the method signature" when
283+
// processing the comparison between the trait and impl fn, but we sadly lose them
284+
// and point at the whole signature when a trait bound or specific input or output
285+
// type would be more appropriate. In other places we have a `Vec<Span>`
286+
// corresponding to their `Vec<Predicate>`, but we don't have that here.
287+
// Fixing this would improve the output of test `issue-83765.rs`.
283288
inh.register_predicates(obligations);
284289
},
285290
);
@@ -1385,12 +1390,13 @@ pub fn check_type_bounds<'tcx>(
13851390

13861391
let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
13871392
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
1388-
let mk_cause = |span| {
1389-
ObligationCause::new(
1390-
impl_ty_span,
1391-
impl_ty_hir_id,
1392-
ObligationCauseCode::BindingObligation(trait_ty.def_id, span),
1393-
)
1393+
let mk_cause = |span: Span| {
1394+
let code = if span.is_dummy() {
1395+
traits::MiscObligation
1396+
} else {
1397+
traits::BindingObligation(trait_ty.def_id, span)
1398+
};
1399+
ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
13941400
};
13951401

13961402
let obligations = tcx

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

+4-42
Original file line numberDiff line numberDiff line change
@@ -586,38 +586,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
586586
}
587587
}
588588

589-
/// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each
590-
/// type/region parameter was instantiated (`substs`), creates and registers suitable
591-
/// trait/region obligations.
592-
///
593-
/// For example, if there is a function:
594-
///
595-
/// ```
596-
/// fn foo<'a,T:'a>(...)
597-
/// ```
598-
///
599-
/// and a reference:
600-
///
601-
/// ```
602-
/// let f = foo;
603-
/// ```
604-
///
605-
/// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a`
606-
/// and `T`. This routine will add a region obligation `$1:'$0` and register it locally.
607-
pub fn add_obligations_for_parameters(
608-
&self,
609-
cause: traits::ObligationCause<'tcx>,
610-
predicates: ty::InstantiatedPredicates<'tcx>,
611-
) {
612-
assert!(!predicates.has_escaping_bound_vars());
613-
614-
debug!("add_obligations_for_parameters(predicates={:?})", predicates);
615-
616-
for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) {
617-
self.register_predicate(obligation);
618-
}
619-
}
620-
621589
// FIXME(arielb1): use this instead of field.ty everywhere
622590
// Only for fields! Returns <none> for methods>
623591
// Indifferent to privacy flags
@@ -1522,20 +1490,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15221490

15231491
/// Add all the obligations that are required, substituting and normalized appropriately.
15241492
#[tracing::instrument(level = "debug", skip(self, span, def_id, substs))]
1525-
fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
1526-
let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs);
1493+
crate fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
1494+
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
15271495

1528-
for (i, mut obligation) in traits::predicates_for_generics(
1496+
for obligation in traits::predicates_for_generics(
15291497
traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
15301498
self.param_env,
15311499
bounds,
1532-
)
1533-
.enumerate()
1534-
{
1535-
// This makes the error point at the bound, but we want to point at the argument
1536-
if let Some(span) = spans.get(i) {
1537-
obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span);
1538-
}
1500+
) {
15391501
self.register_predicate(obligation);
15401502
}
15411503
}

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

+1-4
Original file line numberDiff line numberDiff line change
@@ -509,10 +509,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
509509
self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
510510

511511
// Check bounds on type arguments used in the path.
512-
let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
513-
let cause =
514-
traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did));
515-
self.add_obligations_for_parameters(cause, bounds);
512+
self.add_required_obligations(path_span, did, substs);
516513

517514
Some((variant, ty))
518515
} else {

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

+18-6
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
120120
// We won't add these if we encountered an illegal sized bound, so that we can use
121121
// a custom error in that case.
122122
if illegal_sized_bound.is_none() {
123-
self.add_obligations(self.tcx.mk_fn_ptr(method_sig), all_substs, method_predicates);
123+
self.add_obligations(
124+
self.tcx.mk_fn_ptr(method_sig),
125+
all_substs,
126+
method_predicates,
127+
pick.item.def_id,
128+
);
124129
}
125130

126131
// Create the final `MethodCallee`.
@@ -471,16 +476,23 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
471476
fty: Ty<'tcx>,
472477
all_substs: SubstsRef<'tcx>,
473478
method_predicates: ty::InstantiatedPredicates<'tcx>,
479+
def_id: DefId,
474480
) {
475481
debug!(
476-
"add_obligations: fty={:?} all_substs={:?} method_predicates={:?}",
477-
fty, all_substs, method_predicates
482+
"add_obligations: fty={:?} all_substs={:?} method_predicates={:?} def_id={:?}",
483+
fty, all_substs, method_predicates, def_id
478484
);
479485

480-
self.add_obligations_for_parameters(
481-
traits::ObligationCause::misc(self.span, self.body_id),
486+
// FIXME: could replace with the following, but we already calculated `method_predicates`,
487+
// so we just call `predicates_for_generics` directly to avoid redoing work.
488+
// `self.add_required_obligations(self.span, def_id, &all_substs);`
489+
for obligation in traits::predicates_for_generics(
490+
traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)),
491+
self.param_env,
482492
method_predicates,
483-
);
493+
) {
494+
self.register_predicate(obligation);
495+
}
484496

485497
// this is a projection from a trait reference, so we have to
486498
// make sure that the trait reference inputs are well-formed.

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub use self::CandidateSource::*;
1212
pub use self::MethodError::*;
1313

1414
use crate::check::FnCtxt;
15+
use crate::ObligationCause;
1516
use rustc_data_structures::sync::Lrc;
1617
use rustc_errors::{Applicability, DiagnosticBuilder};
1718
use rustc_hir as hir;
@@ -71,7 +72,8 @@ pub enum MethodError<'tcx> {
7172
#[derive(Debug)]
7273
pub struct NoMatchData<'tcx> {
7374
pub static_candidates: Vec<CandidateSource>,
74-
pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
75+
pub unsatisfied_predicates:
76+
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
7577
pub out_of_scope_traits: Vec<DefId>,
7678
pub lev_candidate: Option<ty::AssocItem>,
7779
pub mode: probe::Mode,
@@ -80,7 +82,11 @@ pub struct NoMatchData<'tcx> {
8082
impl<'tcx> NoMatchData<'tcx> {
8183
pub fn new(
8284
static_candidates: Vec<CandidateSource>,
83-
unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
85+
unsatisfied_predicates: Vec<(
86+
ty::Predicate<'tcx>,
87+
Option<ty::Predicate<'tcx>>,
88+
Option<ObligationCause<'tcx>>,
89+
)>,
8490
out_of_scope_traits: Vec<DefId>,
8591
lev_candidate: Option<ty::AssocItem>,
8692
mode: probe::Mode,

0 commit comments

Comments
 (0)