extract a best_blame_constraint helper
This commit is contained in:
@@ -28,7 +28,7 @@ mod var_name;
|
|||||||
/// Constraints that are considered interesting can be categorized to
|
/// Constraints that are considered interesting can be categorized to
|
||||||
/// determine why they are interesting. Order of variants indicates
|
/// determine why they are interesting. Order of variants indicates
|
||||||
/// sort order of the category, thereby influencing diagnostic output.
|
/// sort order of the category, thereby influencing diagnostic output.
|
||||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
enum ConstraintCategory {
|
enum ConstraintCategory {
|
||||||
Cast,
|
Cast,
|
||||||
Assignment,
|
Assignment,
|
||||||
@@ -43,12 +43,14 @@ enum ConstraintCategory {
|
|||||||
impl fmt::Display for ConstraintCategory {
|
impl fmt::Display for ConstraintCategory {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ConstraintCategory::Assignment |
|
ConstraintCategory::Assignment | ConstraintCategory::AssignmentToUpvar => {
|
||||||
ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
|
write!(f, "assignment")
|
||||||
|
}
|
||||||
ConstraintCategory::Return => write!(f, "return"),
|
ConstraintCategory::Return => write!(f, "return"),
|
||||||
ConstraintCategory::Cast => write!(f, "cast"),
|
ConstraintCategory::Cast => write!(f, "cast"),
|
||||||
ConstraintCategory::CallArgument |
|
ConstraintCategory::CallArgument | ConstraintCategory::CallArgumentToUpvar => {
|
||||||
ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"),
|
write!(f, "argument")
|
||||||
|
}
|
||||||
_ => write!(f, "free region"),
|
_ => write!(f, "free region"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,6 +64,43 @@ enum Trace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
|
/// Tries to find the best constraint to blame for the fact that
|
||||||
|
/// `R: from_region`, where `R` is some region that meets
|
||||||
|
/// `target_test`. This works by following the constraint graph,
|
||||||
|
/// creating a constraint path that forces `R` to outlive
|
||||||
|
/// `from_region`, and then finding the best choices within that
|
||||||
|
/// path to blame.
|
||||||
|
fn best_blame_constraint(
|
||||||
|
&self,
|
||||||
|
mir: &Mir<'tcx>,
|
||||||
|
from_region: RegionVid,
|
||||||
|
target_test: impl Fn(RegionVid) -> bool,
|
||||||
|
) -> (ConstraintCategory, Span) {
|
||||||
|
debug!("best_blame_constraint(from_region={:?})", from_region);
|
||||||
|
|
||||||
|
// Find all paths
|
||||||
|
let path = self
|
||||||
|
.find_constraint_paths_between_regions(from_region, target_test)
|
||||||
|
.unwrap();
|
||||||
|
debug!("best_blame_constraint: path={:#?}", path);
|
||||||
|
|
||||||
|
// Classify each of the constraints along the path.
|
||||||
|
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path
|
||||||
|
.iter()
|
||||||
|
.map(|&index| self.classify_constraint(index, mir))
|
||||||
|
.collect();
|
||||||
|
debug!(
|
||||||
|
"best_blame_constraint: categorized_path={:?}",
|
||||||
|
categorized_path
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find what appears to be the most interesting path to report to the user.
|
||||||
|
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
|
||||||
|
debug!("best_blame_constraint: sorted_path={:?}", categorized_path);
|
||||||
|
|
||||||
|
*categorized_path.first().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Walks the graph of constraints (where `'a: 'b` is considered
|
/// Walks the graph of constraints (where `'a: 'b` is considered
|
||||||
/// an edge `'a -> 'b`) to find all paths from `from_region` to
|
/// an edge `'a -> 'b`) to find all paths from `from_region` to
|
||||||
/// `to_region`. The paths are accumulated into the vector
|
/// `to_region`. The paths are accumulated into the vector
|
||||||
@@ -89,7 +128,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
let mut p = r;
|
let mut p = r;
|
||||||
loop {
|
loop {
|
||||||
match context[p] {
|
match context[p] {
|
||||||
Trace::NotVisited => bug!("found unvisited region {:?} on path to {:?}", p, r),
|
Trace::NotVisited => {
|
||||||
|
bug!("found unvisited region {:?} on path to {:?}", p, r)
|
||||||
|
}
|
||||||
Trace::FromConstraint(c) => {
|
Trace::FromConstraint(c) => {
|
||||||
result.push(c);
|
result.push(c);
|
||||||
p = self.constraints[c].sup;
|
p = self.constraints[c].sup;
|
||||||
@@ -139,19 +180,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
&self,
|
&self,
|
||||||
index: ConstraintIndex,
|
index: ConstraintIndex,
|
||||||
mir: &Mir<'tcx>,
|
mir: &Mir<'tcx>,
|
||||||
_infcx: &InferCtxt<'_, '_, 'tcx>,
|
|
||||||
) -> (ConstraintCategory, Span) {
|
) -> (ConstraintCategory, Span) {
|
||||||
let constraint = self.constraints[index];
|
let constraint = self.constraints[index];
|
||||||
debug!("classify_constraint: constraint={:?}", constraint);
|
debug!("classify_constraint: constraint={:?}", constraint);
|
||||||
let span = constraint.locations.span(mir);
|
let span = constraint.locations.span(mir);
|
||||||
let location = constraint.locations.from_location().unwrap_or(Location::START);
|
let location = constraint
|
||||||
|
.locations
|
||||||
|
.from_location()
|
||||||
|
.unwrap_or(Location::START);
|
||||||
|
|
||||||
if !self.constraint_is_interesting(index) {
|
if !self.constraint_is_interesting(index) {
|
||||||
return (ConstraintCategory::Boring, span);
|
return (ConstraintCategory::Boring, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = &mir[location.block];
|
let data = &mir[location.block];
|
||||||
debug!("classify_constraint: location={:?} data={:?}", location, data);
|
debug!(
|
||||||
|
"classify_constraint: location={:?} data={:?}",
|
||||||
|
location, data
|
||||||
|
);
|
||||||
let category = if location.statement_index == data.statements.len() {
|
let category = if location.statement_index == data.statements.len() {
|
||||||
if let Some(ref terminator) = data.terminator {
|
if let Some(ref terminator) = data.terminator {
|
||||||
debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
|
debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
|
||||||
@@ -174,8 +220,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
} else {
|
} else {
|
||||||
match rvalue {
|
match rvalue {
|
||||||
Rvalue::Cast(..) => ConstraintCategory::Cast,
|
Rvalue::Cast(..) => ConstraintCategory::Cast,
|
||||||
Rvalue::Use(..) |
|
Rvalue::Use(..) | Rvalue::Aggregate(..) => {
|
||||||
Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
|
ConstraintCategory::Assignment
|
||||||
|
}
|
||||||
_ => ConstraintCategory::Other,
|
_ => ConstraintCategory::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,27 +253,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
) {
|
) {
|
||||||
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||||
|
|
||||||
// Find all paths
|
let (category, span) = self.best_blame_constraint(mir, fr, |r| r == outlived_fr);
|
||||||
let path = self.find_constraint_paths_between_regions(fr, |r| r == outlived_fr).unwrap();
|
|
||||||
debug!("report_error: path={:#?}", path);
|
|
||||||
|
|
||||||
// Classify each of the constraints along the path.
|
|
||||||
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
|
|
||||||
.map(|&index| self.classify_constraint(index, mir, infcx))
|
|
||||||
.collect();
|
|
||||||
debug!("report_error: categorized_path={:?}", categorized_path);
|
|
||||||
|
|
||||||
// Find what appears to be the most interesting path to report to the user.
|
|
||||||
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
|
|
||||||
debug!("report_error: sorted_path={:?}", categorized_path);
|
|
||||||
|
|
||||||
// Get a span
|
|
||||||
let (category, span) = categorized_path.first().unwrap();
|
|
||||||
|
|
||||||
// Check if we can use one of the "nice region errors".
|
// Check if we can use one of the "nice region errors".
|
||||||
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
|
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
|
||||||
let tables = infcx.tcx.typeck_tables_of(mir_def_id);
|
let tables = infcx.tcx.typeck_tables_of(mir_def_id);
|
||||||
let nice = NiceRegionError::new_from_span(infcx.tcx, *span, o, f, Some(tables));
|
let nice = NiceRegionError::new_from_span(infcx.tcx, span, o, f, Some(tables));
|
||||||
if let Some(_error_reported) = nice.try_report() {
|
if let Some(_error_reported) = nice.try_report() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -237,22 +269,36 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
self.universal_regions.is_local_free_region(fr),
|
self.universal_regions.is_local_free_region(fr),
|
||||||
self.universal_regions.is_local_free_region(outlived_fr),
|
self.universal_regions.is_local_free_region(outlived_fr),
|
||||||
) {
|
) {
|
||||||
(ConstraintCategory::Assignment, true, false) =>
|
(ConstraintCategory::Assignment, true, false) => ConstraintCategory::AssignmentToUpvar,
|
||||||
&ConstraintCategory::AssignmentToUpvar,
|
(ConstraintCategory::CallArgument, true, false) => {
|
||||||
(ConstraintCategory::CallArgument, true, false) =>
|
ConstraintCategory::CallArgumentToUpvar
|
||||||
&ConstraintCategory::CallArgumentToUpvar,
|
}
|
||||||
(category, _, _) => category,
|
(category, _, _) => category,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("report_error: category={:?}", category);
|
debug!("report_error: category={:?}", category);
|
||||||
match category {
|
match category {
|
||||||
ConstraintCategory::AssignmentToUpvar |
|
ConstraintCategory::AssignmentToUpvar | ConstraintCategory::CallArgumentToUpvar => self
|
||||||
ConstraintCategory::CallArgumentToUpvar =>
|
.report_closure_error(
|
||||||
self.report_closure_error(
|
mir,
|
||||||
mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
|
infcx,
|
||||||
_ =>
|
mir_def_id,
|
||||||
self.report_general_error(
|
fr,
|
||||||
mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
|
outlived_fr,
|
||||||
|
category,
|
||||||
|
span,
|
||||||
|
errors_buffer,
|
||||||
|
),
|
||||||
|
_ => self.report_general_error(
|
||||||
|
mir,
|
||||||
|
infcx,
|
||||||
|
mir_def_id,
|
||||||
|
fr,
|
||||||
|
outlived_fr,
|
||||||
|
category,
|
||||||
|
span,
|
||||||
|
errors_buffer,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,23 +309,31 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
mir_def_id: DefId,
|
mir_def_id: DefId,
|
||||||
fr: RegionVid,
|
fr: RegionVid,
|
||||||
outlived_fr: RegionVid,
|
outlived_fr: RegionVid,
|
||||||
category: &ConstraintCategory,
|
category: ConstraintCategory,
|
||||||
span: &Span,
|
span: Span,
|
||||||
errors_buffer: &mut Vec<Diagnostic>,
|
errors_buffer: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
let fr_name_and_span = self.get_var_name_and_span_for_region(
|
let fr_name_and_span = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr);
|
||||||
infcx.tcx, mir, fr);
|
let outlived_fr_name_and_span =
|
||||||
let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
|
self.get_var_name_and_span_for_region(infcx.tcx, mir, outlived_fr);
|
||||||
infcx.tcx, mir,outlived_fr);
|
|
||||||
|
|
||||||
if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
|
if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
|
||||||
return self.report_general_error(
|
return self.report_general_error(
|
||||||
mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer);
|
mir,
|
||||||
|
infcx,
|
||||||
|
mir_def_id,
|
||||||
|
fr,
|
||||||
|
outlived_fr,
|
||||||
|
category,
|
||||||
|
span,
|
||||||
|
errors_buffer,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut diag = infcx.tcx.sess.struct_span_err(
|
let mut diag = infcx
|
||||||
*span, &format!("borrowed data escapes outside of closure"),
|
.tcx
|
||||||
);
|
.sess
|
||||||
|
.struct_span_err(span, &format!("borrowed data escapes outside of closure"));
|
||||||
|
|
||||||
if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span {
|
if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span {
|
||||||
if let Some(name) = outlived_fr_name {
|
if let Some(name) = outlived_fr_name {
|
||||||
@@ -294,10 +348,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
if let Some(name) = fr_name {
|
if let Some(name) = fr_name {
|
||||||
diag.span_label(
|
diag.span_label(
|
||||||
fr_span,
|
fr_span,
|
||||||
format!("`{}` is a reference that is only valid in the closure body", name),
|
format!(
|
||||||
|
"`{}` is a reference that is only valid in the closure body",
|
||||||
|
name
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
diag.span_label(*span, format!("`{}` escapes the closure body here", name));
|
diag.span_label(span, format!("`{}` escapes the closure body here", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,24 +368,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||||||
mir_def_id: DefId,
|
mir_def_id: DefId,
|
||||||
fr: RegionVid,
|
fr: RegionVid,
|
||||||
outlived_fr: RegionVid,
|
outlived_fr: RegionVid,
|
||||||
category: &ConstraintCategory,
|
category: ConstraintCategory,
|
||||||
span: &Span,
|
span: Span,
|
||||||
errors_buffer: &mut Vec<Diagnostic>,
|
errors_buffer: &mut Vec<Diagnostic>,
|
||||||
) {
|
) {
|
||||||
let mut diag = infcx.tcx.sess.struct_span_err(
|
let mut diag = infcx.tcx.sess.struct_span_err(
|
||||||
*span, &format!("unsatisfied lifetime constraints"), // FIXME
|
span,
|
||||||
|
&format!("unsatisfied lifetime constraints"), // FIXME
|
||||||
);
|
);
|
||||||
|
|
||||||
let counter = &mut 1;
|
let counter = &mut 1;
|
||||||
let fr_name = self.give_region_a_name(
|
let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
|
||||||
infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
|
let outlived_fr_name =
|
||||||
let outlived_fr_name = self.give_region_a_name(
|
self.give_region_a_name(infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);
|
||||||
infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);
|
|
||||||
|
|
||||||
diag.span_label(*span, format!(
|
diag.span_label(
|
||||||
"{} requires that `{}` must outlive `{}`",
|
span,
|
||||||
category, fr_name, outlived_fr_name,
|
format!(
|
||||||
));
|
"{} requires that `{}` must outlive `{}`",
|
||||||
|
category, fr_name, outlived_fr_name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
diag.buffer(errors_buffer);
|
diag.buffer(errors_buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user