Better errors for implied static bound
This commit is contained in:
parent
ff623ffc39
commit
c75817b0a7
23 changed files with 345 additions and 99 deletions
|
@ -15,7 +15,7 @@ use rustc_middle::ty::{self, RegionVid, TyCtxt};
|
|||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::{sym, DesugaringKind, Span};
|
||||
|
||||
use crate::region_infer::BlameConstraint;
|
||||
use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
|
||||
use crate::{
|
||||
borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt,
|
||||
WriteKind,
|
||||
|
@ -38,6 +38,7 @@ pub(crate) enum BorrowExplanation<'tcx> {
|
|||
span: Span,
|
||||
region_name: RegionName,
|
||||
opt_place_desc: Option<String>,
|
||||
extra_info: Vec<ExtraConstraintInfo>,
|
||||
},
|
||||
Unexplained,
|
||||
}
|
||||
|
@ -243,6 +244,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
ref region_name,
|
||||
ref opt_place_desc,
|
||||
from_closure: _,
|
||||
ref extra_info,
|
||||
} => {
|
||||
region_name.highlight_region_name(err);
|
||||
|
||||
|
@ -268,6 +270,14 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
);
|
||||
};
|
||||
|
||||
for extra in extra_info {
|
||||
match extra {
|
||||
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
|
||||
err.span_note(*span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
|
||||
}
|
||||
_ => {}
|
||||
|
@ -309,16 +319,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
&self,
|
||||
borrow_region: RegionVid,
|
||||
outlived_region: RegionVid,
|
||||
) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>) {
|
||||
let BlameConstraint { category, from_closure, cause, variance_info: _ } = self
|
||||
.regioncx
|
||||
.best_blame_constraint(borrow_region, NllRegionVariableOrigin::FreeRegion, |r| {
|
||||
self.regioncx.provides_universal_region(r, borrow_region, outlived_region)
|
||||
});
|
||||
) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) {
|
||||
let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
|
||||
borrow_region,
|
||||
NllRegionVariableOrigin::FreeRegion,
|
||||
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
|
||||
);
|
||||
let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
|
||||
|
||||
let outlived_fr_name = self.give_region_a_name(outlived_region);
|
||||
|
||||
(category, from_closure, cause.span, outlived_fr_name)
|
||||
(category, from_closure, cause.span, outlived_fr_name, extra_info)
|
||||
}
|
||||
|
||||
/// Returns structured explanation for *why* the borrow contains the
|
||||
|
@ -390,7 +401,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
|
||||
None => {
|
||||
if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
|
||||
let (category, from_closure, span, region_name) =
|
||||
let (category, from_closure, span, region_name, extra_info) =
|
||||
self.free_region_constraint_info(borrow_region_vid, region);
|
||||
if let Some(region_name) = region_name {
|
||||
let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
|
||||
|
@ -400,6 +411,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
span,
|
||||
region_name,
|
||||
opt_place_desc,
|
||||
extra_info,
|
||||
}
|
||||
} else {
|
||||
debug!("Could not generate a region name");
|
||||
|
|
|
@ -354,10 +354,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
) {
|
||||
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||
|
||||
let BlameConstraint { category, cause, variance_info, from_closure: _ } =
|
||||
self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
|
||||
let BlameConstraint { category, cause, variance_info, .. } = self
|
||||
.regioncx
|
||||
.best_blame_constraint(fr, fr_origin, |r| {
|
||||
self.regioncx.provides_universal_region(r, fr, outlived_fr)
|
||||
});
|
||||
})
|
||||
.0;
|
||||
|
||||
debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
|
||||
|
||||
|
|
|
@ -245,6 +245,11 @@ enum Trace<'tcx> {
|
|||
NotVisited,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum ExtraConstraintInfo {
|
||||
PlaceholderFromPredicate(Span),
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Creates a new region inference context with a total of
|
||||
/// `num_region_variables` valid inference variables; the first N
|
||||
|
@ -1818,10 +1823,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
fr1_origin: NllRegionVariableOrigin,
|
||||
fr2: RegionVid,
|
||||
) -> (ConstraintCategory<'tcx>, ObligationCause<'tcx>) {
|
||||
let BlameConstraint { category, cause, .. } =
|
||||
self.best_blame_constraint(fr1, fr1_origin, |r| {
|
||||
self.provides_universal_region(r, fr1, fr2)
|
||||
});
|
||||
let BlameConstraint { category, cause, .. } = self
|
||||
.best_blame_constraint(fr1, fr1_origin, |r| self.provides_universal_region(r, fr1, fr2))
|
||||
.0;
|
||||
(category, cause)
|
||||
}
|
||||
|
||||
|
@ -2010,7 +2014,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
from_region: RegionVid,
|
||||
from_region_origin: NllRegionVariableOrigin,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> BlameConstraint<'tcx> {
|
||||
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
|
||||
// Find all paths
|
||||
let (path, target_region) =
|
||||
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
|
||||
|
@ -2026,6 +2030,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
let mut extra_info = vec![];
|
||||
for constraint in path.iter() {
|
||||
let outlived = constraint.sub;
|
||||
let Some(origin) = self.var_infos.get(outlived) else { continue; };
|
||||
let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin else { continue; };
|
||||
debug!(?constraint, ?p);
|
||||
let ConstraintCategory::Predicate(span) = constraint.category else { continue; };
|
||||
extra_info.push(ExtraConstraintInfo::PlaceholderFromPredicate(span));
|
||||
// We only want to point to one
|
||||
break;
|
||||
}
|
||||
|
||||
// We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint.
|
||||
// Instead, we use it to produce an improved `ObligationCauseCode`.
|
||||
// FIXME - determine what we should do if we encounter multiple `ConstraintCategory::Predicate`
|
||||
|
@ -2073,6 +2089,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
from_closure,
|
||||
cause: ObligationCause::new(span, CRATE_HIR_ID, cause_code),
|
||||
variance_info: constraint.variance_info,
|
||||
outlives_constraint: *constraint,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
@ -2174,7 +2191,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let best_choice =
|
||||
if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
|
||||
|
||||
debug!(?best_choice, ?blame_source);
|
||||
debug!(?best_choice, ?blame_source, ?extra_info);
|
||||
|
||||
if let Some(i) = best_choice {
|
||||
if let Some(next) = categorized_path.get(i + 1) {
|
||||
|
@ -2183,7 +2200,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
{
|
||||
// The return expression is being influenced by the return type being
|
||||
// impl Trait, point at the return type and not the return expr.
|
||||
return next.clone();
|
||||
return (next.clone(), extra_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2203,7 +2220,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
return categorized_path[i].clone();
|
||||
return (categorized_path[i].clone(), extra_info);
|
||||
}
|
||||
|
||||
// If that search fails, that is.. unusual. Maybe everything
|
||||
|
@ -2213,7 +2230,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
categorized_path.sort_by(|p0, p1| p0.category.cmp(&p1.category));
|
||||
debug!("sorted_path={:#?}", categorized_path);
|
||||
|
||||
categorized_path.remove(0)
|
||||
(categorized_path.remove(0), extra_info)
|
||||
}
|
||||
|
||||
pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
|
||||
|
@ -2295,7 +2312,13 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
|
|||
outlives_requirement={:?}",
|
||||
region, outlived_region, outlives_requirement,
|
||||
);
|
||||
ty::Binder::dummy(ty::OutlivesPredicate(region.into(), outlived_region))
|
||||
(
|
||||
ty::Binder::dummy(ty::OutlivesPredicate(
|
||||
region.into(),
|
||||
outlived_region,
|
||||
)),
|
||||
ConstraintCategory::BoringNoLocation,
|
||||
)
|
||||
}
|
||||
|
||||
ClosureOutlivesSubject::Ty(ty) => {
|
||||
|
@ -2305,7 +2328,10 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
|
|||
outlives_requirement={:?}",
|
||||
ty, outlived_region, outlives_requirement,
|
||||
);
|
||||
ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), outlived_region))
|
||||
(
|
||||
ty::Binder::dummy(ty::OutlivesPredicate(ty.into(), outlived_region)),
|
||||
ConstraintCategory::BoringNoLocation,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -2319,4 +2345,5 @@ pub struct BlameConstraint<'tcx> {
|
|||
pub from_closure: bool,
|
||||
pub cause: ObligationCause<'tcx>,
|
||||
pub variance_info: ty::VarianceDiagInfo<'tcx>,
|
||||
pub outlives_constraint: OutlivesConstraint<'tcx>,
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
/// constraints should occur within this method so that those
|
||||
/// constraints can be properly localized!**
|
||||
#[instrument(skip(self, op), level = "trace")]
|
||||
pub(super) fn fully_perform_op<R, Op>(
|
||||
pub(super) fn fully_perform_op<R: fmt::Debug, Op>(
|
||||
&mut self,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory<'tcx>,
|
||||
|
@ -39,6 +39,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
|
||||
let TypeOpOutput { output, constraints, error_info } = op.fully_perform(self.infcx)?;
|
||||
|
||||
debug!(?output, ?constraints);
|
||||
|
||||
if let Some(data) = constraints {
|
||||
self.push_region_constraints(locations, category, data);
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) {
|
||||
fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) {
|
||||
debug!("generate: constraints at: {:#?}", self.locations);
|
||||
|
||||
// Extract out various useful fields we'll need below.
|
||||
|
@ -98,15 +98,18 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
// region constraints like `for<'a> 'a: 'b`. At some point
|
||||
// when we move to universes, we will, and this assertion
|
||||
// will start to fail.
|
||||
let ty::OutlivesPredicate(k1, r2) = query_constraint.no_bound_vars().unwrap_or_else(|| {
|
||||
bug!("query_constraint {:?} contained bound vars", query_constraint,);
|
||||
});
|
||||
let ty::OutlivesPredicate(k1, r2) =
|
||||
query_constraint.0.no_bound_vars().unwrap_or_else(|| {
|
||||
bug!("query_constraint {:?} contained bound vars", query_constraint,);
|
||||
});
|
||||
|
||||
let constraint_category = query_constraint.1;
|
||||
|
||||
match k1.unpack() {
|
||||
GenericArgKind::Lifetime(r1) => {
|
||||
let r1_vid = self.to_region_vid(r1);
|
||||
let r2_vid = self.to_region_vid(r2);
|
||||
self.add_outlives(r1_vid, r2_vid);
|
||||
self.add_outlives(r1_vid, r2_vid, constraint_category);
|
||||
}
|
||||
|
||||
GenericArgKind::Type(t1) => {
|
||||
|
@ -121,7 +124,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
Some(implicit_region_bound),
|
||||
param_env,
|
||||
)
|
||||
.type_must_outlive(origin, t1, r2);
|
||||
.type_must_outlive(origin, t1, r2, constraint_category);
|
||||
}
|
||||
|
||||
GenericArgKind::Const(_) => {
|
||||
|
@ -168,10 +171,19 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) {
|
||||
fn add_outlives(
|
||||
&mut self,
|
||||
sup: ty::RegionVid,
|
||||
sub: ty::RegionVid,
|
||||
category: ConstraintCategory<'tcx>,
|
||||
) {
|
||||
let category = match self.category {
|
||||
ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation => category,
|
||||
_ => self.category,
|
||||
};
|
||||
self.constraints.outlives_constraints.push(OutlivesConstraint {
|
||||
locations: self.locations,
|
||||
category: self.category,
|
||||
category,
|
||||
span: self.span,
|
||||
sub,
|
||||
sup,
|
||||
|
@ -191,10 +203,11 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
|
|||
_origin: SubregionOrigin<'tcx>,
|
||||
a: ty::Region<'tcx>,
|
||||
b: ty::Region<'tcx>,
|
||||
constraint_category: ConstraintCategory<'tcx>,
|
||||
) {
|
||||
let b = self.to_region_vid(b);
|
||||
let a = self.to_region_vid(a);
|
||||
self.add_outlives(b, a);
|
||||
self.add_outlives(b, a, constraint_category);
|
||||
}
|
||||
|
||||
fn push_verify(
|
||||
|
|
|
@ -2560,7 +2560,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
.enumerate()
|
||||
.filter_map(|(idx, constraint)| {
|
||||
let ty::OutlivesPredicate(k1, r2) =
|
||||
constraint.no_bound_vars().unwrap_or_else(|| {
|
||||
constraint.0.no_bound_vars().unwrap_or_else(|| {
|
||||
bug!("query_constraint {:?} contained bound vars", constraint,);
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue