extract a `best_blame_constraint` helper
This commit is contained in:
parent
9ba4d33e43
commit
078220daa8
|
@ -28,7 +28,7 @@ mod var_name;
|
|||
/// Constraints that are considered interesting can be categorized to
|
||||
/// determine why they are interesting. Order of variants indicates
|
||||
/// 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 {
|
||||
Cast,
|
||||
Assignment,
|
||||
|
@ -43,12 +43,14 @@ enum ConstraintCategory {
|
|||
impl fmt::Display for ConstraintCategory {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ConstraintCategory::Assignment |
|
||||
ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
|
||||
ConstraintCategory::Assignment | ConstraintCategory::AssignmentToUpvar => {
|
||||
write!(f, "assignment")
|
||||
}
|
||||
ConstraintCategory::Return => write!(f, "return"),
|
||||
ConstraintCategory::Cast => write!(f, "cast"),
|
||||
ConstraintCategory::CallArgument |
|
||||
ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"),
|
||||
ConstraintCategory::CallArgument | ConstraintCategory::CallArgumentToUpvar => {
|
||||
write!(f, "argument")
|
||||
}
|
||||
_ => write!(f, "free region"),
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +64,43 @@ enum Trace {
|
|||
}
|
||||
|
||||
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
|
||||
/// an edge `'a -> 'b`) to find all paths from `from_region` to
|
||||
/// `to_region`. The paths are accumulated into the vector
|
||||
|
@ -89,7 +128,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let mut p = r;
|
||||
loop {
|
||||
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) => {
|
||||
result.push(c);
|
||||
p = self.constraints[c].sup;
|
||||
|
@ -139,19 +180,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
&self,
|
||||
index: ConstraintIndex,
|
||||
mir: &Mir<'tcx>,
|
||||
_infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
) -> (ConstraintCategory, Span) {
|
||||
let constraint = self.constraints[index];
|
||||
debug!("classify_constraint: constraint={:?}", constraint);
|
||||
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) {
|
||||
return (ConstraintCategory::Boring, span);
|
||||
}
|
||||
|
||||
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() {
|
||||
if let Some(ref terminator) = data.terminator {
|
||||
debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
|
||||
|
@ -174,8 +220,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
} else {
|
||||
match rvalue {
|
||||
Rvalue::Cast(..) => ConstraintCategory::Cast,
|
||||
Rvalue::Use(..) |
|
||||
Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
|
||||
Rvalue::Use(..) | Rvalue::Aggregate(..) => {
|
||||
ConstraintCategory::Assignment
|
||||
}
|
||||
_ => ConstraintCategory::Other,
|
||||
}
|
||||
}
|
||||
|
@ -206,27 +253,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
) {
|
||||
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||
|
||||
// Find all paths
|
||||
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();
|
||||
let (category, span) = self.best_blame_constraint(mir, fr, |r| r == outlived_fr);
|
||||
|
||||
// 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)) {
|
||||
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() {
|
||||
return;
|
||||
}
|
||||
|
@ -237,22 +269,36 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
self.universal_regions.is_local_free_region(fr),
|
||||
self.universal_regions.is_local_free_region(outlived_fr),
|
||||
) {
|
||||
(ConstraintCategory::Assignment, true, false) =>
|
||||
&ConstraintCategory::AssignmentToUpvar,
|
||||
(ConstraintCategory::CallArgument, true, false) =>
|
||||
&ConstraintCategory::CallArgumentToUpvar,
|
||||
(ConstraintCategory::Assignment, true, false) => ConstraintCategory::AssignmentToUpvar,
|
||||
(ConstraintCategory::CallArgument, true, false) => {
|
||||
ConstraintCategory::CallArgumentToUpvar
|
||||
}
|
||||
(category, _, _) => category,
|
||||
};
|
||||
|
||||
debug!("report_error: category={:?}", category);
|
||||
match category {
|
||||
ConstraintCategory::AssignmentToUpvar |
|
||||
ConstraintCategory::CallArgumentToUpvar =>
|
||||
self.report_closure_error(
|
||||
mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
|
||||
_ =>
|
||||
self.report_general_error(
|
||||
mir, infcx, mir_def_id, fr, outlived_fr, category, span, errors_buffer),
|
||||
ConstraintCategory::AssignmentToUpvar | ConstraintCategory::CallArgumentToUpvar => self
|
||||
.report_closure_error(
|
||||
mir,
|
||||
infcx,
|
||||
mir_def_id,
|
||||
fr,
|
||||
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,
|
||||
fr: RegionVid,
|
||||
outlived_fr: RegionVid,
|
||||
category: &ConstraintCategory,
|
||||
span: &Span,
|
||||
category: ConstraintCategory,
|
||||
span: Span,
|
||||
errors_buffer: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
let fr_name_and_span = self.get_var_name_and_span_for_region(
|
||||
infcx.tcx, mir, fr);
|
||||
let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
|
||||
infcx.tcx, mir,outlived_fr);
|
||||
let fr_name_and_span = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr);
|
||||
let outlived_fr_name_and_span =
|
||||
self.get_var_name_and_span_for_region(infcx.tcx, mir, outlived_fr);
|
||||
|
||||
if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
|
||||
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(
|
||||
*span, &format!("borrowed data escapes outside of closure"),
|
||||
);
|
||||
let mut diag = infcx
|
||||
.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(name) = outlived_fr_name {
|
||||
|
@ -294,10 +348,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
if let Some(name) = fr_name {
|
||||
diag.span_label(
|
||||
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,
|
||||
fr: RegionVid,
|
||||
outlived_fr: RegionVid,
|
||||
category: &ConstraintCategory,
|
||||
span: &Span,
|
||||
category: ConstraintCategory,
|
||||
span: Span,
|
||||
errors_buffer: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
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 fr_name = self.give_region_a_name(
|
||||
infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
|
||||
let outlived_fr_name = self.give_region_a_name(
|
||||
infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);
|
||||
let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, &mut diag);
|
||||
let outlived_fr_name =
|
||||
self.give_region_a_name(infcx.tcx, mir, mir_def_id, outlived_fr, counter, &mut diag);
|
||||
|
||||
diag.span_label(*span, format!(
|
||||
"{} requires that `{}` must outlive `{}`",
|
||||
category, fr_name, outlived_fr_name,
|
||||
));
|
||||
diag.span_label(
|
||||
span,
|
||||
format!(
|
||||
"{} requires that `{}` must outlive `{}`",
|
||||
category, fr_name, outlived_fr_name,
|
||||
),
|
||||
);
|
||||
|
||||
diag.buffer(errors_buffer);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue