Skip to content

Commit 994dc4b

Browse files
committed
Classify outlives constraints when type checking
The MIR/NLL type checker is in a much better position to classify constraints and already has to classify into boring and interesting. Adds spans to Locations::All for error reporting Adds more constraint categories
1 parent 6e42521 commit 994dc4b

File tree

12 files changed

+377
-304
lines changed

12 files changed

+377
-304
lines changed

src/librustc/infer/opaque_types/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
284284
}
285285
}
286286

287-
fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
287+
pub fn constrain_opaque_type<FRR: FreeRegionRelations<'tcx>>(
288288
&self,
289289
def_id: DefId,
290290
opaque_defn: &OpaqueTypeDecl<'tcx>,

src/librustc_mir/borrow_check/nll/constraints/graph.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
// except according to those terms.
1010

1111
use borrow_check::nll::type_check::Locations;
12-
use borrow_check::nll::constraints::{ConstraintIndex, ConstraintSet, OutlivesConstraint};
12+
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintIndex};
13+
use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
1314
use rustc::ty::RegionVid;
1415
use rustc_data_structures::graph;
1516
use rustc_data_structures::indexed_vec::IndexVec;
17+
use syntax_pos::DUMMY_SP;
1618

1719
/// The construct graph organizes the constraints by their end-points.
1820
/// It can be used to view a `R1: R2` constraint as either an edge `R1
@@ -174,7 +176,8 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
174176
Some(OutlivesConstraint {
175177
sup: self.static_region,
176178
sub: next_static_idx.into(),
177-
locations: Locations::All,
179+
locations: Locations::All(DUMMY_SP),
180+
category: ConstraintCategory::Internal,
178181
})
179182
} else {
180183
None

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

+39
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,42 @@ crate struct ConstraintSet {
2323
constraints: IndexVec<ConstraintIndex, OutlivesConstraint>,
2424
}
2525

26+
/// Constraints can be categorized to determine whether and why they are
27+
/// interesting. Order of variants indicates sort order of the category,
28+
/// thereby influencing diagnostic output.
29+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
30+
pub enum ConstraintCategory {
31+
Return,
32+
TypeAnnotation,
33+
Cast,
34+
CallArgument,
35+
36+
/// A constraint that came from checking the body of a closure.
37+
///
38+
/// Ideally we would give an explanation that points to the relevant part
39+
/// of the closure's body.
40+
ClosureBounds,
41+
CopyBound,
42+
SizedBound,
43+
Assignment,
44+
OpaqueType,
45+
46+
/// A "boring" constraint (caused by the given location) is one that
47+
/// the user probably doesn't want to see described in diagnostics,
48+
/// because it is kind of an artifact of the type system setup.
49+
/// Example: `x = Foo { field: y }` technically creates
50+
/// intermediate regions representing the "type of `Foo { field: y
51+
/// }`", and data flows from `y` into those variables, but they
52+
/// are not very interesting. The assignment into `x` on the other
53+
/// hand might be.
54+
Boring,
55+
// Boring and applicable everywhere.
56+
BoringNoLocation,
57+
58+
/// A constraint that doesn't correspond to anything the user sees.
59+
Internal,
60+
}
61+
2662
impl ConstraintSet {
2763
crate fn push(&mut self, constraint: OutlivesConstraint) {
2864
debug!(
@@ -87,6 +123,9 @@ pub struct OutlivesConstraint {
87123

88124
/// Where did this constraint arise?
89125
pub locations: Locations,
126+
127+
/// What caused this constraint?
128+
pub category: ConstraintCategory,
90129
}
91130

92131
impl fmt::Debug for OutlivesConstraint {

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
8888
sup,
8989
sub,
9090
locations,
91+
category,
9192
} = constraint;
9293
with_msg(&format!(
93-
"{:?}: {:?} due to {:?}",
94+
"{:?}: {:?} due to {:?} at {:?}",
9495
sup,
9596
sub,
97+
category,
9698
locations,
9799
))?;
98100
}

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

+17-129
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use borrow_check::nll::constraints::OutlivesConstraint;
11+
use borrow_check::nll::constraints::{OutlivesConstraint, ConstraintCategory};
1212
use borrow_check::nll::region_infer::RegionInferenceContext;
13-
use borrow_check::nll::type_check::Locations;
1413
use rustc::hir::def_id::DefId;
1514
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
1615
use rustc::infer::InferCtxt;
17-
use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
18-
use rustc::ty::{self, TyCtxt, RegionVid};
16+
use rustc::mir::{Location, Mir};
17+
use rustc::ty::{self, RegionVid};
1918
use rustc_data_structures::indexed_vec::IndexVec;
2019
use rustc_errors::{Diagnostic, DiagnosticBuilder};
2120
use std::collections::VecDeque;
@@ -28,19 +27,6 @@ mod var_name;
2827

2928
use self::region_name::RegionName;
3029

31-
/// Constraints that are considered interesting can be categorized to
32-
/// determine why they are interesting. Order of variants indicates
33-
/// sort order of the category, thereby influencing diagnostic output.
34-
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
35-
enum ConstraintCategory {
36-
Cast,
37-
Assignment,
38-
Return,
39-
CallArgument,
40-
Other,
41-
Boring,
42-
}
43-
4430
impl fmt::Display for ConstraintCategory {
4531
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4632
// Must end with a space. Allows for empty names to be provided.
@@ -49,7 +35,14 @@ impl fmt::Display for ConstraintCategory {
4935
ConstraintCategory::Return => write!(f, "returning this value "),
5036
ConstraintCategory::Cast => write!(f, "cast "),
5137
ConstraintCategory::CallArgument => write!(f, "argument "),
52-
_ => write!(f, ""),
38+
ConstraintCategory::TypeAnnotation => write!(f, "type annotation "),
39+
ConstraintCategory::ClosureBounds => write!(f, "closure body "),
40+
ConstraintCategory::SizedBound => write!(f, "proving this value is `Sized` "),
41+
ConstraintCategory::CopyBound => write!(f, "copying this value "),
42+
ConstraintCategory::OpaqueType => write!(f, "opaque type "),
43+
ConstraintCategory::Boring
44+
| ConstraintCategory::BoringNoLocation
45+
| ConstraintCategory::Internal => write!(f, ""),
5346
}
5447
}
5548
}
@@ -71,7 +64,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
7164
fn best_blame_constraint(
7265
&self,
7366
mir: &Mir<'tcx>,
74-
tcx: TyCtxt<'_, '_, 'tcx>,
7567
from_region: RegionVid,
7668
target_test: impl Fn(RegionVid) -> bool,
7769
) -> (ConstraintCategory, Span, RegionVid) {
@@ -96,7 +88,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
9688
// Classify each of the constraints along the path.
9789
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path
9890
.iter()
99-
.map(|&index| self.classify_constraint(index, mir, tcx))
91+
.map(|constraint| (constraint.category, constraint.locations.span(mir)))
10092
.collect();
10193
debug!(
10294
"best_blame_constraint: categorized_path={:#?}",
@@ -129,12 +121,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
129121
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
130122

131123
match categorized_path[i].0 {
132-
ConstraintCategory::Boring => false,
133-
ConstraintCategory::Other => {
134-
// other isn't interesting when the two lifetimes
135-
// are unified.
136-
constraint_sup_scc != self.constraint_sccs.scc(constraint.sub)
137-
}
124+
ConstraintCategory::OpaqueType
125+
| ConstraintCategory::Boring
126+
| ConstraintCategory::BoringNoLocation
127+
| ConstraintCategory::Internal => false,
138128
_ => constraint_sup_scc != target_scc,
139129
}
140130
});
@@ -220,106 +210,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
220210
None
221211
}
222212

223-
/// This function will return true if a constraint is interesting and false if a constraint
224-
/// is not. It is useful in filtering constraint paths to only interesting points.
225-
fn constraint_is_interesting(&self, constraint: OutlivesConstraint) -> bool {
226-
debug!(
227-
"constraint_is_interesting: locations={:?} constraint={:?}",
228-
constraint.locations, constraint
229-
);
230-
231-
match constraint.locations {
232-
Locations::Interesting(_) | Locations::All => true,
233-
_ => false,
234-
}
235-
}
236-
237-
/// This function classifies a constraint from a location.
238-
fn classify_constraint(
239-
&self,
240-
constraint: OutlivesConstraint,
241-
mir: &Mir<'tcx>,
242-
tcx: TyCtxt<'_, '_, 'tcx>,
243-
) -> (ConstraintCategory, Span) {
244-
debug!("classify_constraint: constraint={:?}", constraint);
245-
let span = constraint.locations.span(mir);
246-
let location = constraint
247-
.locations
248-
.from_location()
249-
.unwrap_or(Location::START);
250-
251-
if !self.constraint_is_interesting(constraint) {
252-
return (ConstraintCategory::Boring, span);
253-
}
254-
255-
let data = &mir[location.block];
256-
debug!(
257-
"classify_constraint: location={:?} data={:?}",
258-
location, data
259-
);
260-
let category = if location.statement_index == data.statements.len() {
261-
if let Some(ref terminator) = data.terminator {
262-
debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
263-
match terminator.kind {
264-
TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
265-
// Classify calls differently depending on whether or not
266-
// the sub region appears in the destination type (so the
267-
// sup region is in the return type). If the return type
268-
// contains the sub-region, then this is either an
269-
// assignment or a return, depending on whether we are
270-
// writing to the RETURN_PLACE or not.
271-
//
272-
// The idea here is that the region is being propagated
273-
// from an input into the output place, so it's a kind of
274-
// assignment. Otherwise, if the sub-region only appears in
275-
// the argument types, then use the CallArgument
276-
// classification.
277-
TerminatorKind::Call { destination: Some((ref place, _)), .. } => {
278-
if tcx.any_free_region_meets(
279-
&place.ty(mir, tcx).to_ty(tcx),
280-
|region| self.to_region_vid(region) == constraint.sub,
281-
) {
282-
match place {
283-
Place::Local(mir::RETURN_PLACE) => ConstraintCategory::Return,
284-
_ => ConstraintCategory::Assignment,
285-
}
286-
} else {
287-
ConstraintCategory::CallArgument
288-
}
289-
}
290-
TerminatorKind::Call { destination: None, .. } => {
291-
ConstraintCategory::CallArgument
292-
}
293-
_ => ConstraintCategory::Other,
294-
}
295-
} else {
296-
ConstraintCategory::Other
297-
}
298-
} else {
299-
let statement = &data.statements[location.statement_index];
300-
debug!("classify_constraint: statement.kind={:?}", statement.kind);
301-
match statement.kind {
302-
StatementKind::Assign(ref place, ref rvalue) => {
303-
debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
304-
if *place == Place::Local(mir::RETURN_PLACE) {
305-
ConstraintCategory::Return
306-
} else {
307-
match rvalue {
308-
Rvalue::Cast(..) => ConstraintCategory::Cast,
309-
Rvalue::Use(..) | Rvalue::Aggregate(..) => {
310-
ConstraintCategory::Assignment
311-
}
312-
_ => ConstraintCategory::Other,
313-
}
314-
}
315-
}
316-
_ => ConstraintCategory::Other,
317-
}
318-
};
319-
320-
(category, span)
321-
}
322-
323213
/// Report an error because the universal region `fr` was required to outlive
324214
/// `outlived_fr` but it is not known to do so. For example:
325215
///
@@ -341,7 +231,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
341231

342232
let (category, span, _) = self.best_blame_constraint(
343233
mir,
344-
infcx.tcx,
345234
fr,
346235
|r| r == outlived_fr
347236
);
@@ -574,11 +463,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
574463
crate fn find_outlives_blame_span(
575464
&self,
576465
mir: &Mir<'tcx>,
577-
tcx: TyCtxt<'_, '_, 'tcx>,
578466
fr1: RegionVid,
579467
fr2: RegionVid,
580468
) -> Span {
581-
let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
469+
let (_, span, _) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
582470
span
583471
}
584472
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
10621062
longer_fr, shorter_fr,
10631063
);
10641064

1065-
let blame_span = self.find_outlives_blame_span(mir, infcx.tcx, longer_fr, shorter_fr);
1065+
let blame_span = self.find_outlives_blame_span(mir, longer_fr, shorter_fr);
10661066

10671067
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
10681068
// Shrink `fr` until we find a non-local region (if we do).
@@ -1147,7 +1147,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
11471147
};
11481148

11491149
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
1150-
let span = self.find_outlives_blame_span(mir, infcx.tcx, longer_fr, error_region);
1150+
let span = self.find_outlives_blame_span(mir, longer_fr, error_region);
11511151

11521152
// Obviously, this error message is far from satisfactory.
11531153
// At present, though, it only appears in unit tests --

src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use borrow_check::location::LocationTable;
12-
use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
12+
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
1313
use borrow_check::nll::facts::AllFacts;
1414
use borrow_check::nll::region_infer::{RegionTest, TypeTest};
1515
use borrow_check::nll::type_check::Locations;
@@ -30,6 +30,7 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> {
3030
implicit_region_bound: Option<ty::Region<'tcx>>,
3131
param_env: ty::ParamEnv<'tcx>,
3232
locations: Locations,
33+
category: ConstraintCategory,
3334
outlives_constraints: &'a mut ConstraintSet,
3435
type_tests: &'a mut Vec<TypeTest<'tcx>>,
3536
all_facts: &'a mut Option<AllFacts>,
@@ -44,6 +45,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
4445
implicit_region_bound: Option<ty::Region<'tcx>>,
4546
param_env: ty::ParamEnv<'tcx>,
4647
locations: Locations,
48+
category: ConstraintCategory,
4749
outlives_constraints: &'a mut ConstraintSet,
4850
type_tests: &'a mut Vec<TypeTest<'tcx>>,
4951
all_facts: &'a mut Option<AllFacts>,
@@ -56,6 +58,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
5658
implicit_region_bound,
5759
param_env,
5860
locations,
61+
category,
5962
outlives_constraints,
6063
type_tests,
6164
all_facts,
@@ -183,6 +186,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
183186
fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) {
184187
self.outlives_constraints.push(OutlivesConstraint {
185188
locations: self.locations,
189+
category: self.category,
186190
sub,
187191
sup,
188192
});

0 commit comments

Comments
 (0)