Skip to content

Commit 576b640

Browse files
committed
Auto merge of #54262 - matthewjasper:explain-in-typeck, r=nikomatsakis
[NLL] Record more infomation on free region constraints in typeck Changes: * Makes the span of the MIR return place point to the return type * Don't try to use a path to a type alias as a path to the adt it aliases (fixes an ICE) * Don't claim that `self` is declared outside of the function. [see this test](f2995d5#diff-0c9e6b1b204f42129b481df9ce459d44) * Remove boring/interesting distinction and instead add a `ConstraintCategory` to the constraint. * Add categories for implicit `Sized` and `Copy` requirements, for closure bounds, for user type annotations and `impl Trait`. * Don't use the span of the first statement for Locations::All bounds (even if it happens to work on the tests we have) Future work: * Fine tuning the heuristic used to choose the place the report the error. * Reporting multiple places (behind a flag) * Better closure bounds reporting. This probably requires some discussion. r? @nikomatsakis
2 parents c6e3d7f + bd0895d commit 576b640

File tree

70 files changed

+746
-540
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+746
-540
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

+22-130
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;
@@ -29,19 +28,6 @@ mod var_name;
2928

3029
use self::region_name::RegionName;
3130

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

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

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

343233
let (category, span, _) = self.best_blame_constraint(
344234
mir,
345-
infcx.tcx,
346235
fr,
347236
|r| r == outlived_fr
348237
);
@@ -392,7 +281,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
392281

393282
let escapes_from = if infcx.tcx.is_closure(mir_def_id) { "closure" } else { "function" };
394283

395-
if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
284+
// Revert to the normal error in these cases.
285+
// Assignments aren't "escapes" in function items.
286+
if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
287+
|| (category == ConstraintCategory::Assignment && escapes_from == "function")
288+
{
396289
return self.report_general_error(mir, infcx, mir_def_id,
397290
fr, true, outlived_fr, false,
398291
category, span, errors_buffer);
@@ -572,11 +465,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
572465
crate fn find_outlives_blame_span(
573466
&self,
574467
mir: &Mir<'tcx>,
575-
tcx: TyCtxt<'_, '_, 'tcx>,
576468
fr1: RegionVid,
577469
fr2: RegionVid,
578470
) -> Span {
579-
let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
471+
let (_, span, _) = self.best_blame_constraint(mir, fr1, |r| r == fr2);
580472
span
581473
}
582474
}

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

+17-10
Original file line numberDiff line numberDiff line change
@@ -452,16 +452,23 @@ impl<'tcx> RegionInferenceContext<'tcx> {
452452
ty::Adt(_adt_def, substs),
453453
hir::TyKind::Path(hir::QPath::Resolved(None, path)),
454454
) => {
455-
if let Some(last_segment) = path.segments.last() {
456-
if let Some(name) = self.match_adt_and_segment(
457-
substs,
458-
needle_fr,
459-
last_segment,
460-
counter,
461-
diag,
462-
search_stack,
463-
) {
464-
return Some(name);
455+
match path.def {
456+
// Type parameters of the type alias have no reason to
457+
// be the same as those of the ADT.
458+
// FIXME: We should be able to do something similar to
459+
// match_adt_and_segment in this case.
460+
hir::def::Def::TyAlias(_) => (),
461+
_ => if let Some(last_segment) = path.segments.last() {
462+
if let Some(name) = self.match_adt_and_segment(
463+
substs,
464+
needle_fr,
465+
last_segment,
466+
counter,
467+
diag,
468+
search_stack,
469+
) {
470+
return Some(name);
471+
}
465472
}
466473
}
467474
}

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 --

0 commit comments

Comments
 (0)