Skip to content

Commit 6afee11

Browse files
committed
Auto merge of #133858 - dianne:better-blame-constraints-for-static, r=lcnr
`best_blame_constraint`: Blame better constraints when the region graph has cycles from invariance or `'static` This fixes #132749 by changing which constraint is blamed for region errors in several cases. `best_blame_constraint` had a heuristic that tried to pinpoint the constraint causing an error by filtering out any constraints where the outliving region is unified with the ultimate target region being outlived. However, it used the SCCs of the region graph to do this, which is unreliable; in particular, if the target region is `'static`, or if there are cycles from the presence of invariant types, it was skipping over the constraints it should be blaming. As is the case in that issue, this could lead to confusing diagnostics. The simplest fix seems to work decently, judging by test stderr: this makes `best_blame_constraint` no longer filter constraints by their outliving region's SCC. There are admittedly some quirks in the test output. In many cases, subdiagnostics that depend on the particular constraint being blamed have either started or stopped being emitted. After starting at this for quite a while, I think anything too fickle about whether it outputs based on the particular constraint being blamed should instead be looking at the constraint path as a whole, similar to what's done for [the placeholder-from-predicate note](master...dianne:rust:better-blame-constraints-for-static#diff-3c0de6462469af483c9ecdf2c4b00cb26192218ef2d5c62a0fde75107a74caaeR506). Very many tests involving invariant types gained a note pointing out the types' invariance, but in a few cases it was lost. A particularly illustrative example is [tests/ui/lifetimes/copy_modulo_regions.stderr](https://github.com/rust-lang/rust/compare/master...dianne:rust:better-blame-constraints-for-static?expand=1#diff-96e1f8b29789b3c4ce2f77a5e0fba248829b97ef9d1ce39e7d2b4aa57b2cf4f0); I'd argue the new constraint is a better one to blame, but it lacks the variance diagnostic information that's elsewhere in the constraint path. If desired, I can try making that note check the whole path rather than just the blamed constraint. The subdiagnostic [`BorrowExplanation::add_object_lifetime_default_note`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/diagnostics/explain_borrow/enum.BorrowExplanation.html#method.add_object_lifetime_default_note) depends on a `Cast` being blamed, so [a special case](364ca7f) was necessary to keep it from disappearing from tests specifically testing for it. However, see the FIXME comment in that commit; I think the special case should be removed once that subdiagnostic works properly, but it's nontrivial enough to warrant a separate PR. Incidentally, this removes the note from a test where it was being added erroneously: in [tests/ui/borrowck/two-phase-surprise-no-conflict.stderr](https://github.com/rust-lang/rust/compare/master...dianne:rust:better-blame-constraints-for-static?expand=1#diff-8cf085af8203677de6575a45458c9e6b03412a927df879412adec7e4f7ff5e14), the object lifetime is explicitly provided and it's not `'static`.
2 parents 9c87288 + fe8b12f commit 6afee11

File tree

99 files changed

+731
-620
lines changed

Some content is hidden

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

99 files changed

+731
-620
lines changed

Cargo.lock

-1
Original file line numberDiff line numberDiff line change
@@ -4115,7 +4115,6 @@ name = "rustc_middle"
41154115
version = "0.0.0"
41164116
dependencies = [
41174117
"bitflags",
4118-
"derive-where",
41194118
"either",
41204119
"field-offset",
41214120
"gsgdt",

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+9-66
Original file line numberDiff line numberDiff line change
@@ -1516,15 +1516,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
15161516
});
15171517

15181518
self.explain_why_borrow_contains_point(location, borrow, None)
1519-
.add_explanation_to_diagnostic(
1520-
self.infcx.tcx,
1521-
self.body,
1522-
&self.local_names,
1523-
&mut err,
1524-
"",
1525-
Some(borrow_span),
1526-
None,
1527-
);
1519+
.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
15281520
self.suggest_copy_for_type_in_cloned_ref(&mut err, place);
15291521
let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
15301522
if let Some(expr) = self.find_expr(borrow_span) {
@@ -1591,15 +1583,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
15911583
});
15921584

15931585
self.explain_why_borrow_contains_point(location, borrow, None)
1594-
.add_explanation_to_diagnostic(
1595-
self.infcx.tcx,
1596-
self.body,
1597-
&self.local_names,
1598-
&mut err,
1599-
"",
1600-
None,
1601-
None,
1602-
);
1586+
.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
16031587
err
16041588
}
16051589

@@ -1886,9 +1870,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
18861870
}
18871871

18881872
explanation.add_explanation_to_diagnostic(
1889-
self.infcx.tcx,
1890-
self.body,
1891-
&self.local_names,
1873+
&self,
18921874
&mut err,
18931875
first_borrow_desc,
18941876
None,
@@ -3046,15 +3028,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
30463028

30473029
if let BorrowExplanation::MustBeValidFor { .. } = explanation {
30483030
} else {
3049-
explanation.add_explanation_to_diagnostic(
3050-
self.infcx.tcx,
3051-
self.body,
3052-
&self.local_names,
3053-
&mut err,
3054-
"",
3055-
None,
3056-
None,
3057-
);
3031+
explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
30583032
}
30593033
} else {
30603034
err.span_label(borrow_span, "borrowed value does not live long enough");
@@ -3067,15 +3041,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
30673041
}
30683042
});
30693043

3070-
explanation.add_explanation_to_diagnostic(
3071-
self.infcx.tcx,
3072-
self.body,
3073-
&self.local_names,
3074-
&mut err,
3075-
"",
3076-
Some(borrow_span),
3077-
None,
3078-
);
3044+
explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
30793045
}
30803046

30813047
err
@@ -3128,15 +3094,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
31283094
_ => {}
31293095
}
31303096

3131-
explanation.add_explanation_to_diagnostic(
3132-
self.infcx.tcx,
3133-
self.body,
3134-
&self.local_names,
3135-
&mut err,
3136-
"",
3137-
None,
3138-
None,
3139-
);
3097+
explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
31403098

31413099
self.buffer_error(err);
31423100
}
@@ -3309,15 +3267,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
33093267
}
33103268
_ => {}
33113269
}
3312-
explanation.add_explanation_to_diagnostic(
3313-
self.infcx.tcx,
3314-
self.body,
3315-
&self.local_names,
3316-
&mut err,
3317-
"",
3318-
None,
3319-
None,
3320-
);
3270+
explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
33213271

33223272
borrow_spans.args_subdiag(&mut err, |args_span| {
33233273
crate::session_diagnostics::CaptureArgLabel::Capture {
@@ -3808,15 +3758,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
38083758
}
38093759
});
38103760

3811-
self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic(
3812-
self.infcx.tcx,
3813-
self.body,
3814-
&self.local_names,
3815-
&mut err,
3816-
"",
3817-
None,
3818-
None,
3819-
);
3761+
self.explain_why_borrow_contains_point(location, loan, None)
3762+
.add_explanation_to_diagnostic(&self, &mut err, "", None, None);
38203763

38213764
self.explain_deref_coercion(loan, &mut err);
38223765

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+18-20
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use std::assert_matches::assert_matches;
88
use rustc_errors::{Applicability, Diag};
99
use rustc_hir as hir;
1010
use rustc_hir::intravisit::Visitor;
11-
use rustc_index::IndexSlice;
1211
use rustc_infer::infer::NllRegionVariableOrigin;
1312
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
1413
use rustc_middle::mir::{
@@ -18,14 +17,15 @@ use rustc_middle::mir::{
1817
use rustc_middle::ty::adjustment::PointerCoercion;
1918
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
2019
use rustc_middle::util::CallKind;
21-
use rustc_span::{DesugaringKind, Span, Symbol, kw, sym};
20+
use rustc_span::{DesugaringKind, Span, kw, sym};
2221
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
2322
use tracing::{debug, instrument};
2423

2524
use super::{RegionName, UseSpans, find_use};
2625
use crate::borrow_set::BorrowData;
26+
use crate::constraints::OutlivesConstraint;
2727
use crate::nll::ConstraintDescription;
28-
use crate::region_infer::{BlameConstraint, Cause, ExtraConstraintInfo};
28+
use crate::region_infer::{BlameConstraint, Cause};
2929
use crate::{MirBorrowckCtxt, WriteKind};
3030

3131
#[derive(Debug)]
@@ -43,7 +43,7 @@ pub(crate) enum BorrowExplanation<'tcx> {
4343
span: Span,
4444
region_name: RegionName,
4545
opt_place_desc: Option<String>,
46-
extra_info: Vec<ExtraConstraintInfo>,
46+
path: Vec<OutlivesConstraint<'tcx>>,
4747
},
4848
Unexplained,
4949
}
@@ -63,14 +63,16 @@ impl<'tcx> BorrowExplanation<'tcx> {
6363
}
6464
pub(crate) fn add_explanation_to_diagnostic(
6565
&self,
66-
tcx: TyCtxt<'tcx>,
67-
body: &Body<'tcx>,
68-
local_names: &IndexSlice<Local, Option<Symbol>>,
66+
cx: &MirBorrowckCtxt<'_, '_, 'tcx>,
6967
err: &mut Diag<'_>,
7068
borrow_desc: &str,
7169
borrow_span: Option<Span>,
7270
multiple_borrow_span: Option<(Span, Span)>,
7371
) {
72+
let tcx = cx.infcx.tcx;
73+
let body = cx.body;
74+
let local_names = &cx.local_names;
75+
7476
if let Some(span) = borrow_span {
7577
let def_id = body.source.def_id();
7678
if let Some(node) = tcx.hir().get_if_local(def_id)
@@ -306,7 +308,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
306308
ref region_name,
307309
ref opt_place_desc,
308310
from_closure: _,
309-
ref extra_info,
311+
ref path,
310312
} => {
311313
region_name.highlight_region_name(err);
312314

@@ -328,13 +330,8 @@ impl<'tcx> BorrowExplanation<'tcx> {
328330
);
329331
};
330332

331-
for extra in extra_info {
332-
match extra {
333-
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
334-
err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
335-
}
336-
}
337-
}
333+
cx.add_placeholder_from_predicate_note(err, &path);
334+
cx.add_sized_or_copy_bound_info(err, category, &path);
338335

339336
if let ConstraintCategory::Cast {
340337
is_implicit_coercion: true,
@@ -487,8 +484,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
487484
&self,
488485
borrow_region: RegionVid,
489486
outlived_region: RegionVid,
490-
) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) {
491-
let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
487+
) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<OutlivesConstraint<'tcx>>)
488+
{
489+
let (blame_constraint, path) = self.regioncx.best_blame_constraint(
492490
borrow_region,
493491
NllRegionVariableOrigin::FreeRegion,
494492
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
@@ -497,7 +495,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
497495

498496
let outlived_fr_name = self.give_region_a_name(outlived_region);
499497

500-
(category, from_closure, cause.span, outlived_fr_name, extra_info)
498+
(category, from_closure, cause.span, outlived_fr_name, path)
501499
}
502500

503501
/// Returns structured explanation for *why* the borrow contains the
@@ -596,7 +594,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
596594

597595
None => {
598596
if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
599-
let (category, from_closure, span, region_name, extra_info) =
597+
let (category, from_closure, span, region_name, path) =
600598
self.free_region_constraint_info(borrow_region_vid, region);
601599
if let Some(region_name) = region_name {
602600
let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
@@ -606,7 +604,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
606604
span,
607605
region_name,
608606
opt_place_desc,
609-
extra_info,
607+
path,
610608
}
611609
} else {
612610
debug!("Could not generate a region name");

compiler/rustc_borrowck/src/diagnostics/mod.rs

+54-4
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ use rustc_errors::{Applicability, Diag, MultiSpan};
88
use rustc_hir::def::{CtorKind, Namespace};
99
use rustc_hir::{self as hir, CoroutineKind, LangItem};
1010
use rustc_index::IndexSlice;
11-
use rustc_infer::infer::BoundRegionConversionTime;
11+
use rustc_infer::infer::{
12+
BoundRegionConversionTime, NllRegionVariableOrigin, RegionVariableOrigin,
13+
};
1214
use rustc_infer::traits::SelectionError;
1315
use rustc_middle::bug;
1416
use rustc_middle::mir::tcx::PlaceTy;
1517
use rustc_middle::mir::{
16-
AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location,
17-
Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
18-
TerminatorKind,
18+
AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
19+
LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement,
20+
StatementKind, Terminator, TerminatorKind,
1921
};
2022
use rustc_middle::ty::print::Print;
2123
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
@@ -33,7 +35,9 @@ use tracing::debug;
3335

3436
use super::MirBorrowckCtxt;
3537
use super::borrow_set::BorrowData;
38+
use crate::constraints::OutlivesConstraint;
3639
use crate::fluent_generated as fluent;
40+
use crate::nll::ConstraintDescription;
3741
use crate::session_diagnostics::{
3842
CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
3943
CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
@@ -619,6 +623,52 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
619623
region.print(&mut printer).unwrap();
620624
printer.into_buffer()
621625
}
626+
627+
/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
628+
/// implicitly introduce an "outlives `'static`" constraint.
629+
fn add_placeholder_from_predicate_note(
630+
&self,
631+
err: &mut Diag<'_>,
632+
path: &[OutlivesConstraint<'tcx>],
633+
) {
634+
let predicate_span = path.iter().find_map(|constraint| {
635+
let outlived = constraint.sub;
636+
if let Some(origin) = self.regioncx.var_infos.get(outlived)
637+
&& let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_)) =
638+
origin.origin
639+
&& let ConstraintCategory::Predicate(span) = constraint.category
640+
{
641+
Some(span)
642+
} else {
643+
None
644+
}
645+
});
646+
647+
if let Some(span) = predicate_span {
648+
err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
649+
}
650+
}
651+
652+
/// Add a label to region errors and borrow explanations when outlives constraints arise from
653+
/// proving a type implements `Sized` or `Copy`.
654+
fn add_sized_or_copy_bound_info(
655+
&self,
656+
err: &mut Diag<'_>,
657+
blamed_category: ConstraintCategory<'tcx>,
658+
path: &[OutlivesConstraint<'tcx>],
659+
) {
660+
for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] {
661+
if sought_category != blamed_category
662+
&& let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category)
663+
{
664+
let label = format!(
665+
"requirement occurs due to {}",
666+
sought_category.description().trim_end()
667+
);
668+
err.span_label(sought_constraint.span, label);
669+
}
670+
}
671+
}
622672
}
623673

624674
/// The span(s) associated to a use of a place.

0 commit comments

Comments
 (0)