Skip to content

Commit 93ab12e

Browse files
committed
Improve cause information for NLL higher-ranked errors
This PR has several interconnected pieces: 1. In some of the NLL region error code, we now pass around an `ObligationCause`, instead of just a plain `Span`. This gets forwarded into `fulfill_cx.register_predicate_obligation` during error reporting. 2. The general InferCtxt error reporting code is extended to handle `ObligationCauseCode::BindingObligation` 3. A new enum variant `ConstraintCategory::Predicate` is added. We try to avoid using this as the 'best blame constraint' - instead, we use it to enhance the `ObligationCause` of the `BlameConstraint` that we do end up choosing. As a result, several NLL error messages now contain the same "the lifetime requirement is introduced here" message as non-NLL errors. Having an `ObligationCause` available will likely prove useful for future improvements to NLL error messages.
1 parent 3e8f32e commit 93ab12e

File tree

16 files changed

+197
-85
lines changed

16 files changed

+197
-85
lines changed

Diff for: compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs

+73-45
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
99
use rustc_span::Span;
1010
use rustc_trait_selection::traits::query::type_op;
1111
use rustc_trait_selection::traits::{SelectionContext, TraitEngineExt as _};
12-
use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_span};
12+
use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause};
1313

1414
use std::fmt;
1515
use std::rc::Rc;
@@ -45,21 +45,20 @@ impl UniverseInfo<'tcx> {
4545
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
4646
placeholder: ty::PlaceholderRegion,
4747
error_element: RegionElement,
48-
span: Span,
48+
cause: ObligationCause<'tcx>,
4949
) {
5050
match self.0 {
5151
UniverseInfoInner::RelateTys { expected, found } => {
52-
let body_id = mbcx.infcx.tcx.hir().local_def_id_to_hir_id(mbcx.mir_def_id());
5352
let err = mbcx.infcx.report_mismatched_types(
54-
&ObligationCause::misc(span, body_id),
53+
&cause,
5554
expected,
5655
found,
5756
TypeError::RegionsPlaceholderMismatch,
5857
);
5958
err.buffer(&mut mbcx.errors_buffer);
6059
}
6160
UniverseInfoInner::TypeOp(ref type_op_info) => {
62-
type_op_info.report_error(mbcx, placeholder, error_element, span);
61+
type_op_info.report_error(mbcx, placeholder, error_element, cause);
6362
}
6463
UniverseInfoInner::Other => {
6564
// FIXME: This error message isn't great, but it doesn't show
@@ -68,7 +67,7 @@ impl UniverseInfo<'tcx> {
6867
mbcx.infcx
6968
.tcx
7069
.sess
71-
.struct_span_err(span, "higher-ranked subtype error")
70+
.struct_span_err(cause.span, "higher-ranked subtype error")
7271
.buffer(&mut mbcx.errors_buffer);
7372
}
7473
}
@@ -130,7 +129,7 @@ trait TypeOpInfo<'tcx> {
130129
fn nice_error(
131130
&self,
132131
tcx: TyCtxt<'tcx>,
133-
span: Span,
132+
cause: ObligationCause<'tcx>,
134133
placeholder_region: ty::Region<'tcx>,
135134
error_region: Option<ty::Region<'tcx>>,
136135
) -> Option<DiagnosticBuilder<'tcx>>;
@@ -140,7 +139,7 @@ trait TypeOpInfo<'tcx> {
140139
mbcx: &mut MirBorrowckCtxt<'_, 'tcx>,
141140
placeholder: ty::PlaceholderRegion,
142141
error_element: RegionElement,
143-
span: Span,
142+
cause: ObligationCause<'tcx>,
144143
) {
145144
let tcx = mbcx.infcx.tcx;
146145
let base_universe = self.base_universe();
@@ -150,7 +149,7 @@ trait TypeOpInfo<'tcx> {
150149
{
151150
adjusted
152151
} else {
153-
self.fallback_error(tcx, span).buffer(&mut mbcx.errors_buffer);
152+
self.fallback_error(tcx, cause.span).buffer(&mut mbcx.errors_buffer);
154153
return;
155154
};
156155

@@ -175,7 +174,8 @@ trait TypeOpInfo<'tcx> {
175174

176175
debug!(?placeholder_region);
177176

178-
let nice_error = self.nice_error(tcx, span, placeholder_region, error_region);
177+
let span = cause.span;
178+
let nice_error = self.nice_error(tcx, cause, placeholder_region, error_region);
179179

180180
if let Some(nice_error) = nice_error {
181181
nice_error.buffer(&mut mbcx.errors_buffer);
@@ -205,15 +205,24 @@ impl TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
205205
fn nice_error(
206206
&self,
207207
tcx: TyCtxt<'tcx>,
208-
span: Span,
208+
cause: ObligationCause<'tcx>,
209209
placeholder_region: ty::Region<'tcx>,
210210
error_region: Option<ty::Region<'tcx>>,
211211
) -> Option<DiagnosticBuilder<'tcx>> {
212-
tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
213-
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
214-
type_op_prove_predicate_with_span(infcx, &mut *fulfill_cx, key, Some(span));
215-
try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
216-
})
212+
tcx.infer_ctxt().enter_with_canonical(
213+
cause.span,
214+
&self.canonical_query,
215+
|ref infcx, key, _| {
216+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
217+
type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause);
218+
try_extract_error_from_fulfill_cx(
219+
fulfill_cx,
220+
infcx,
221+
placeholder_region,
222+
error_region,
223+
)
224+
},
225+
)
217226
}
218227
}
219228

@@ -239,32 +248,41 @@ where
239248
fn nice_error(
240249
&self,
241250
tcx: TyCtxt<'tcx>,
242-
span: Span,
251+
cause: ObligationCause<'tcx>,
243252
placeholder_region: ty::Region<'tcx>,
244253
error_region: Option<ty::Region<'tcx>>,
245254
) -> Option<DiagnosticBuilder<'tcx>> {
246-
tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
247-
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
248-
249-
let mut selcx = SelectionContext::new(infcx);
250-
251-
// FIXME(lqd): Unify and de-duplicate the following with the actual
252-
// `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
253-
// `ObligationCause`. The normalization results are currently different between
254-
// `AtExt::normalize` used in the query and `normalize` called below: the former fails
255-
// to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
256-
// after #85499 lands to see if its fixes have erased this difference.
257-
let (param_env, value) = key.into_parts();
258-
let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize(
259-
&mut selcx,
260-
param_env,
261-
ObligationCause::dummy_with_span(span),
262-
value.value,
263-
);
264-
fulfill_cx.register_predicate_obligations(infcx, obligations);
265-
266-
try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
267-
})
255+
tcx.infer_ctxt().enter_with_canonical(
256+
cause.span,
257+
&self.canonical_query,
258+
|ref infcx, key, _| {
259+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
260+
261+
let mut selcx = SelectionContext::new(infcx);
262+
263+
// FIXME(lqd): Unify and de-duplicate the following with the actual
264+
// `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
265+
// `ObligationCause`. The normalization results are currently different between
266+
// `AtExt::normalize` used in the query and `normalize` called below: the former fails
267+
// to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
268+
// after #85499 lands to see if its fixes have erased this difference.
269+
let (param_env, value) = key.into_parts();
270+
let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize(
271+
&mut selcx,
272+
param_env,
273+
cause,
274+
value.value,
275+
);
276+
fulfill_cx.register_predicate_obligations(infcx, obligations);
277+
278+
try_extract_error_from_fulfill_cx(
279+
fulfill_cx,
280+
infcx,
281+
placeholder_region,
282+
error_region,
283+
)
284+
},
285+
)
268286
}
269287
}
270288

@@ -287,15 +305,25 @@ impl TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
287305
fn nice_error(
288306
&self,
289307
tcx: TyCtxt<'tcx>,
290-
span: Span,
308+
cause: ObligationCause<'tcx>,
291309
placeholder_region: ty::Region<'tcx>,
292310
error_region: Option<ty::Region<'tcx>>,
293311
) -> Option<DiagnosticBuilder<'tcx>> {
294-
tcx.infer_ctxt().enter_with_canonical(span, &self.canonical_query, |ref infcx, key, _| {
295-
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
296-
type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(span)).ok()?;
297-
try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
298-
})
312+
tcx.infer_ctxt().enter_with_canonical(
313+
cause.span,
314+
&self.canonical_query,
315+
|ref infcx, key, _| {
316+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
317+
type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span))
318+
.ok()?;
319+
try_extract_error_from_fulfill_cx(
320+
fulfill_cx,
321+
infcx,
322+
placeholder_region,
323+
error_region,
324+
)
325+
},
326+
)
299327
}
300328
}
301329

Diff for: compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
300300
borrow_region: RegionVid,
301301
outlived_region: RegionVid,
302302
) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
303-
let BlameConstraint { category, from_closure, span, variance_info: _ } =
303+
let BlameConstraint { category, from_closure, cause, variance_info: _ } =
304304
self.regioncx.best_blame_constraint(
305305
&self.body,
306306
borrow_region,
@@ -310,7 +310,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
310310

311311
let outlived_fr_name = self.give_region_a_name(outlived_region);
312312

313-
(category, from_closure, span, outlived_fr_name)
313+
(category, from_closure, cause.span, outlived_fr_name)
314314
}
315315

316316
/// Returns structured explanation for *why* the borrow contains the

Diff for: compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_span::{BytePos, Span};
1313

1414
use crate::borrowck_errors;
1515

16+
use super::{OutlivesSuggestionBuilder, RegionName};
1617
use crate::region_infer::BlameConstraint;
1718
use crate::{
1819
nll::ConstraintDescription,
@@ -21,8 +22,6 @@ use crate::{
2122
MirBorrowckCtxt,
2223
};
2324

24-
use super::{OutlivesSuggestionBuilder, RegionName};
25-
2625
impl ConstraintDescription for ConstraintCategory {
2726
fn description(&self) -> &'static str {
2827
// Must end with a space. Allows for empty names to be provided.
@@ -41,7 +40,8 @@ impl ConstraintDescription for ConstraintCategory {
4140
ConstraintCategory::OpaqueType => "opaque type ",
4241
ConstraintCategory::ClosureUpvar(_) => "closure capture ",
4342
ConstraintCategory::Usage => "this usage ",
44-
ConstraintCategory::Boring
43+
ConstraintCategory::Predicate(_, _)
44+
| ConstraintCategory::Boring
4545
| ConstraintCategory::BoringNoLocation
4646
| ConstraintCategory::Internal => "",
4747
}
@@ -217,7 +217,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
217217
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
218218

219219
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
220-
let (_, span) = self.regioncx.find_outlives_blame_span(
220+
let (_, cause) = self.regioncx.find_outlives_blame_span(
221221
&self.body,
222222
longer_fr,
223223
NllRegionVariableOrigin::Placeholder(placeholder),
@@ -227,7 +227,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
227227
let universe = placeholder.universe;
228228
let universe_info = self.regioncx.universe_info(universe);
229229

230-
universe_info.report_error(self, placeholder, error_element, span);
230+
universe_info.report_error(self, placeholder, error_element, cause);
231231
}
232232

233233
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
@@ -275,15 +275,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
275275
) {
276276
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
277277

278-
let BlameConstraint { category, span, variance_info, from_closure: _ } =
278+
let BlameConstraint { category, cause, variance_info, from_closure: _ } =
279279
self.regioncx.best_blame_constraint(&self.body, fr, fr_origin, |r| {
280280
self.regioncx.provides_universal_region(r, fr, outlived_fr)
281281
});
282282

283-
debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info);
283+
debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
284284
// Check if we can use one of the "nice region errors".
285285
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
286-
let nice = NiceRegionError::new_from_span(self.infcx, span, o, f);
286+
let nice = NiceRegionError::new_from_span(self.infcx, cause.span, o, f);
287287
if let Some(diag) = nice.try_report_from_nll() {
288288
diag.buffer(&mut self.errors_buffer);
289289
return;
@@ -306,7 +306,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
306306
fr_is_local,
307307
outlived_fr_is_local,
308308
category,
309-
span,
309+
span: cause.span,
310310
};
311311

312312
let mut diag = match (category, fr_is_local, outlived_fr_is_local) {

0 commit comments

Comments
 (0)