Skip to content

Commit fd08066

Browse files
committed
Constraints are now being categorized, sorted and the error labelled. Categorization needs a bit of work.
1 parent 56274be commit fd08066

File tree

2 files changed

+116
-16
lines changed

2 files changed

+116
-16
lines changed

src/librustc_mir/borrow_check/nll/constraint_set.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec};
1313
use borrow_check::nll::type_check::Locations;
1414

1515
use std::fmt;
16+
use std::cmp::Ordering;
1617
use std::ops::Deref;
1718

1819
#[derive(Clone, Default)]
@@ -109,4 +110,39 @@ impl fmt::Debug for OutlivesConstraint {
109110
}
110111
}
111112

113+
/// Constraints that are considered interesting can be categorized to
114+
/// determine why they are interesting.
115+
#[derive(Debug, Eq, PartialEq)]
116+
crate enum ConstraintCategory {
117+
Assignment,
118+
CallArgument,
119+
Cast,
120+
Other,
121+
}
122+
123+
impl Ord for ConstraintCategory {
124+
fn cmp(&self, other: &Self) -> Ordering {
125+
if self == other {
126+
return Ordering::Equal;
127+
}
128+
129+
match (self, other) {
130+
(ConstraintCategory::Assignment, _) => Ordering::Greater,
131+
(_, ConstraintCategory::Assignment) => Ordering::Less,
132+
(ConstraintCategory::CallArgument, _) => Ordering::Greater,
133+
(_, ConstraintCategory::CallArgument) => Ordering::Less,
134+
(ConstraintCategory::Cast, _) => Ordering::Greater,
135+
(_, ConstraintCategory::Cast) => Ordering::Less,
136+
(ConstraintCategory::Other, _) => Ordering::Greater,
137+
}
138+
}
139+
}
140+
141+
impl PartialOrd for ConstraintCategory {
142+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
143+
Some(self.cmp(other))
144+
}
145+
}
146+
147+
112148
newtype_index!(ConstraintIndex { DEBUG_FORMAT = "ConstraintIndex({})" });

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
use super::universal_regions::UniversalRegions;
1212
use borrow_check::nll::region_infer::values::ToElementIndex;
13-
use borrow_check::nll::constraint_set::{ConstraintIndex, ConstraintSet, OutlivesConstraint};
13+
use borrow_check::nll::constraint_set::{ConstraintIndex, ConstraintCategory, ConstraintSet};
14+
use borrow_check::nll::constraint_set::{OutlivesConstraint};
1415
use borrow_check::nll::type_check::Locations;
1516
use rustc::hir::def_id::DefId;
1617
use rustc::infer::canonical::QueryRegionConstraint;
@@ -21,13 +22,14 @@ use rustc::infer::NLLRegionVariableOrigin;
2122
use rustc::infer::RegionVariableOrigin;
2223
use rustc::mir::{
2324
ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location,
24-
Mir,
25+
Mir, StatementKind, TerminatorKind, Rvalue
2526
};
2627
use rustc::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable};
2728
use rustc::util::common::{self, ErrorReported};
2829
use rustc_data_structures::bitvec::BitVector;
2930
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
3031
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
32+
3133
use std::rc::Rc;
3234
use syntax_pos::Span;
3335

@@ -961,10 +963,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
961963
// Note: in this case, we use the unapproximated regions
962964
// to report the error. This gives better error messages
963965
// in some cases.
964-
self.report_error(infcx, mir_def_id, longer_fr, shorter_fr, blame_span);
966+
self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, blame_span);
965967
}
966968
}
967969

970+
/// When reporting an error, it is useful to be able to determine which constraints influenced
971+
/// the region being reported as an error. This function finds all of the paths from the
972+
/// constraint.
968973
fn find_constraint_paths_from_region(
969974
&self,
970975
r0: RegionVid
@@ -1055,6 +1060,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
10551060
paths
10561061
}
10571062

1063+
/// This function will return true if a constraint is interesting and false if a constraint
1064+
/// is not. It is useful in filtering constraint paths to only interesting points.
10581065
fn constraint_is_interesting(&self, index: &ConstraintIndex) -> bool {
10591066
self.constraints.get(*index).filter(|constraint| {
10601067
debug!("constraint_is_interesting: locations={:?} constraint={:?}",
@@ -1063,6 +1070,32 @@ impl<'tcx> RegionInferenceContext<'tcx> {
10631070
}).is_some()
10641071
}
10651072

1073+
/// This function classifies a constraint from a location.
1074+
fn classify_constraint(&self, location: Location, mir: &Mir<'tcx>) -> ConstraintCategory {
1075+
let data = &mir[location.block];
1076+
if location.statement_index == data.statements.len() {
1077+
if let Some(ref terminator) = data.terminator {
1078+
match terminator.kind {
1079+
TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
1080+
TerminatorKind::Call { .. } => ConstraintCategory::CallArgument,
1081+
_ => ConstraintCategory::Other,
1082+
}
1083+
} else {
1084+
ConstraintCategory::Other
1085+
}
1086+
} else {
1087+
let statement = &data.statements[location.statement_index];
1088+
match statement.kind {
1089+
StatementKind::Assign(_, ref rvalue) => match rvalue {
1090+
Rvalue::Cast(..) => ConstraintCategory::Cast,
1091+
Rvalue::Use(..) => ConstraintCategory::Assignment,
1092+
_ => ConstraintCategory::Other,
1093+
},
1094+
_ => ConstraintCategory::Other,
1095+
}
1096+
}
1097+
}
1098+
10661099
/// Report an error because the universal region `fr` was required to outlive
10671100
/// `outlived_fr` but it is not known to do so. For example:
10681101
///
@@ -1073,6 +1106,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
10731106
/// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
10741107
fn report_error(
10751108
&self,
1109+
mir: &Mir<'tcx>,
10761110
infcx: &InferCtxt<'_, '_, 'tcx>,
10771111
mir_def_id: DefId,
10781112
fr: RegionVid,
@@ -1095,27 +1129,57 @@ impl<'tcx> RegionInferenceContext<'tcx> {
10951129
let constraints = self.find_constraint_paths_from_region(fr.clone());
10961130
let path = constraints.iter().min_by_key(|p| p.len()).unwrap();
10971131
debug!("report_error: path={:?}", path);
1132+
10981133
let path = path.iter()
10991134
.filter(|index| self.constraint_is_interesting(index))
11001135
.collect::<Vec<&ConstraintIndex>>();
11011136
debug!("report_error: path={:?}", path);
11021137

1103-
let fr_string = match fr_name {
1104-
Some(r) => format!("free region `{}`", r),
1105-
None => format!("free region `{:?}`", fr),
1106-
};
1138+
let mut categorized_path = path.iter().filter_map(|index| {
1139+
self.constraints.get(**index).iter().filter_map(|constraint| {
1140+
let span = constraint.locations.span(mir);
1141+
constraint.locations.from_location().iter().filter_map(|location| {
1142+
let classification = self.classify_constraint(*location, mir);
1143+
Some((classification, span))
1144+
}).next()
1145+
}).next()
1146+
}).collect::<Vec<(ConstraintCategory, Span)>>();
1147+
debug!("report_error: categorized_path={:?}", categorized_path);
1148+
1149+
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
1150+
debug!("report_error: sorted_path={:?}", categorized_path);
1151+
1152+
if categorized_path.len() > 0 {
1153+
let blame_constraint = &categorized_path[0];
1154+
1155+
let mut diag = infcx.tcx.sess.struct_span_err(
1156+
blame_constraint.1,
1157+
&format!("{:?}", blame_constraint.0),
1158+
);
11071159

1108-
let outlived_fr_string = match outlived_fr_name {
1109-
Some(r) => format!("free region `{}`", r),
1110-
None => format!("free region `{:?}`", outlived_fr),
1111-
};
1160+
for secondary in categorized_path.iter().skip(1) {
1161+
diag.span_label(secondary.1, format!("{:?}", secondary.0));
1162+
}
11121163

1113-
let mut diag = infcx.tcx.sess.struct_span_err(
1114-
blame_span,
1115-
&format!("{} does not outlive {}", fr_string, outlived_fr_string,),
1116-
);
1164+
diag.emit();
1165+
} else {
1166+
let fr_string = match fr_name {
1167+
Some(r) => format!("free region `{}`", r),
1168+
None => format!("free region `{:?}`", fr),
1169+
};
1170+
1171+
let outlived_fr_string = match outlived_fr_name {
1172+
Some(r) => format!("free region `{}`", r),
1173+
None => format!("free region `{:?}`", outlived_fr),
1174+
};
1175+
1176+
let mut diag = infcx.tcx.sess.struct_span_err(
1177+
blame_span,
1178+
&format!("{} does not outlive {}", fr_string, outlived_fr_string,),
1179+
);
11171180

1118-
diag.emit();
1181+
diag.emit();
1182+
}
11191183
}
11201184

11211185
crate fn why_region_contains_point(&self, fr1: RegionVid, elem: Location) -> Option<Cause> {

0 commit comments

Comments
 (0)