Skip to content

Commit 93fb9e6

Browse files
authored
Rollup merge of rust-lang#67241 - mark-i-m:simplify-borrow_check-3, r=matthewjasper
Refactorings to borrowck region diagnostic reporting This PR is a followup to rust-lang#66886 and rust-lang#67404 EDIT: updated In this PR: Clean up how errors are collected from NLL: introduce `borrow_check::diagnostics::RegionErrors` to collect errors. This is later passed to `MirBorrowckCtx::report_region_errors` after the `MirBorrowckCtx` is created. This will allow us to refactor away some of the extra context structs floating around (but we don't do it in this PR). `borrow_check::region_infer` is now mostly free of diagnostic generating code. The signatures of most of the functions in `region_infer` lose somewhere between 4 and 7 arguments :) Left for future (feedback appreciated): - Merge `ErrorRegionCtx` with `MirBorrowckCtx`, as suggested by @matthewjasper in rust-lang#66886 (comment) - Maybe move the contents of `borrow_check::nll` into `borrow_check` and remove the `nll` submodule altogether. - Find a way to make `borrow_check::diagnostics::region_errors` less of a strange appendage to `RegionInferenceContext`. I'm not really sure how to do this yet. Ideas welcome.
2 parents 3982d35 + 1d9c561 commit 93fb9e6

File tree

6 files changed

+337
-260
lines changed

6 files changed

+337
-260
lines changed

src/librustc_mir/borrow_check/diagnostics/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ mod explain_borrow;
3232

3333
crate use mutability_errors::AccessKind;
3434
crate use region_name::{RegionName, RegionNameSource, RegionErrorNamingCtx};
35-
crate use region_errors::{ErrorReportingCtx, ErrorConstraintInfo};
35+
crate use region_errors::{ErrorReportingCtx, ErrorConstraintInfo, RegionErrors, RegionErrorKind};
3636
crate use outlives_suggestion::OutlivesSuggestionBuilder;
3737

3838
pub(super) struct IncludingDowncast(pub(super) bool);

src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,9 @@ impl OutlivesSuggestionBuilder<'a> {
250250

251251
// If there is only one constraint to suggest, then we already suggested it in the
252252
// intermediate suggestion above.
253-
if self.constraints_to_add.len() == 1 {
253+
if self.constraints_to_add.len() == 1
254+
&& self.constraints_to_add.values().next().unwrap().len() == 1
255+
{
254256
debug!("Only 1 suggestion. Skipping.");
255257
return;
256258
}

src/librustc_mir/borrow_check/diagnostics/region_errors.rs

+64-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
use rustc::hir::def_id::DefId;
44
use rustc::infer::{
55
error_reporting::nice_region_error::NiceRegionError,
6+
region_constraints::GenericKind,
67
InferCtxt, NLLRegionVariableOrigin,
78
};
89
use rustc::mir::{
910
ConstraintCategory, Local, Location, Body,
1011
};
11-
use rustc::ty::{self, RegionVid};
12+
use rustc::ty::{self, RegionVid, Ty};
1213
use rustc_index::vec::IndexVec;
1314
use rustc_errors::DiagnosticBuilder;
1415
use std::collections::VecDeque;
@@ -53,13 +54,74 @@ impl ConstraintDescription for ConstraintCategory {
5354
}
5455
}
5556

56-
#[derive(Copy, Clone, PartialEq, Eq)]
57+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5758
enum Trace {
5859
StartRegion,
5960
FromOutlivesConstraint(OutlivesConstraint),
6061
NotVisited,
6162
}
6263

64+
/// A collection of errors encountered during region inference. This is needed to efficiently
65+
/// report errors after borrow checking.
66+
///
67+
/// Usually we expect this to either be empty or contain a small number of items, so we can avoid
68+
/// allocation most of the time.
69+
crate type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>;
70+
71+
#[derive(Clone, Debug)]
72+
crate enum RegionErrorKind<'tcx> {
73+
/// An error for a type test: `T: 'a` does not live long enough.
74+
TypeTestDoesNotLiveLongEnough {
75+
/// The span of the type test.
76+
span: Span,
77+
/// The generic type of the type test.
78+
generic: GenericKind<'tcx>,
79+
},
80+
81+
/// A generic bound failure for a type test.
82+
TypeTestGenericBoundError {
83+
/// The span of the type test.
84+
span: Span,
85+
/// The generic type of the type test.
86+
generic: GenericKind<'tcx>,
87+
/// The lower bound region.
88+
lower_bound_region: ty::Region<'tcx>,
89+
},
90+
91+
/// An unexpected hidden region for an opaque type.
92+
UnexpectedHiddenRegion {
93+
/// The def id of the opaque type.
94+
opaque_type_def_id: DefId,
95+
/// The hidden type.
96+
hidden_ty: Ty<'tcx>,
97+
/// The unexpected region.
98+
member_region: ty::Region<'tcx>,
99+
},
100+
101+
/// Higher-ranked subtyping error.
102+
BoundUniversalRegionError {
103+
/// The placeholder free region.
104+
longer_fr: RegionVid,
105+
/// The region that erroneously must be outlived by `longer_fr`.
106+
error_region: RegionVid,
107+
/// The origin of the placeholder region.
108+
fr_origin: NLLRegionVariableOrigin,
109+
},
110+
111+
/// Any other lifetime error.
112+
RegionError {
113+
/// The origin of the region.
114+
fr_origin: NLLRegionVariableOrigin,
115+
/// The region that should outlive `shorter_fr`.
116+
longer_fr: RegionVid,
117+
/// The region that should be shorter, but we can't prove it.
118+
shorter_fr: RegionVid,
119+
/// Indicates whether this is a reported error. We currently only report the first error
120+
/// encountered and leave the rest unreported so as not to overwhelm the user.
121+
is_reported: bool,
122+
},
123+
}
124+
63125
/// Various pieces of state used when reporting borrow checker errors.
64126
pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
65127
/// The region inference context used for borrow chekcing this MIR body.

src/librustc_mir/borrow_check/mod.rs

+155-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use rustc::hir::{self, HirId};
44
use rustc::hir::Node;
55
use rustc::hir::def_id::DefId;
6-
use rustc::infer::InferCtxt;
6+
use rustc::infer::{opaque_types, InferCtxt};
77
use rustc::lint::builtin::UNUSED_MUT;
88
use rustc::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT};
99
use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
@@ -39,12 +39,15 @@ use crate::dataflow::MoveDataParamEnv;
3939
use crate::dataflow::{do_dataflow, DebugFormatted};
4040
use crate::dataflow::EverInitializedPlaces;
4141
use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
42+
use crate::transform::MirSource;
4243

4344
use self::flows::Flows;
4445
use self::location::LocationTable;
4546
use self::prefixes::PrefixSet;
4647
use self::MutateMode::{JustWrite, WriteAndRead};
47-
use self::diagnostics::AccessKind;
48+
use self::diagnostics::{
49+
AccessKind, RegionErrors, RegionErrorKind, OutlivesSuggestionBuilder, RegionErrorNamingCtx,
50+
};
4851

4952
use self::path_utils::*;
5053

@@ -211,20 +214,40 @@ fn do_mir_borrowck<'a, 'tcx>(
211214
let borrow_set = Rc::new(BorrowSet::build(
212215
tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
213216

214-
// If we are in non-lexical mode, compute the non-lexical lifetimes.
215-
let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions(
217+
// Compute non-lexical lifetimes.
218+
let nll::NllOutput {
219+
regioncx, polonius_output, opt_closure_req, nll_errors
220+
} = nll::compute_regions(
216221
infcx,
217222
def_id,
218223
free_regions,
219224
body,
220225
&promoted,
221-
&local_names,
222-
&upvars,
223226
location_table,
224227
param_env,
225228
&mut flow_inits,
226229
&mdpe.move_data,
227230
&borrow_set,
231+
);
232+
233+
// Dump MIR results into a file, if that is enabled. This let us
234+
// write unit-tests, as well as helping with debugging.
235+
nll::dump_mir_results(
236+
infcx,
237+
MirSource::item(def_id),
238+
&body,
239+
&regioncx,
240+
&opt_closure_req,
241+
);
242+
243+
// We also have a `#[rustc_nll]` annotation that causes us to dump
244+
// information.
245+
nll::dump_annotation(
246+
infcx,
247+
&body,
248+
def_id,
249+
&regioncx,
250+
&opt_closure_req,
228251
&mut errors_buffer,
229252
);
230253

@@ -297,6 +320,9 @@ fn do_mir_borrowck<'a, 'tcx>(
297320
local_names,
298321
};
299322

323+
// Compute and report region errors, if any.
324+
mbcx.report_region_errors(nll_errors);
325+
300326
let mut state = Flows::new(
301327
flow_borrows,
302328
flow_uninits,
@@ -1560,6 +1586,129 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15601586
// initial reservation.
15611587
}
15621588
}
1589+
1590+
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
1591+
fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
1592+
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
1593+
// buffered in the `MirBorrowckCtxt`.
1594+
1595+
// FIXME(mark-i-m): Would be great to get rid of the naming context.
1596+
let mut region_naming = RegionErrorNamingCtx::new();
1597+
let mut outlives_suggestion = OutlivesSuggestionBuilder::new(
1598+
self.mir_def_id, &self.local_names
1599+
);
1600+
1601+
for nll_error in nll_errors.into_iter() {
1602+
match nll_error {
1603+
RegionErrorKind::TypeTestDoesNotLiveLongEnough { span, generic } => {
1604+
// FIXME. We should handle this case better. It
1605+
// indicates that we have e.g., some region variable
1606+
// whose value is like `'a+'b` where `'a` and `'b` are
1607+
// distinct unrelated univesal regions that are not
1608+
// known to outlive one another. It'd be nice to have
1609+
// some examples where this arises to decide how best
1610+
// to report it; we could probably handle it by
1611+
// iterating over the universal regions and reporting
1612+
// an error that multiple bounds are required.
1613+
self.infcx.tcx.sess
1614+
.struct_span_err(
1615+
span,
1616+
&format!("`{}` does not live long enough", generic),
1617+
)
1618+
.buffer(&mut self.errors_buffer);
1619+
},
1620+
1621+
RegionErrorKind::TypeTestGenericBoundError {
1622+
span, generic, lower_bound_region
1623+
} => {
1624+
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
1625+
self.infcx
1626+
.construct_generic_bound_failure(
1627+
region_scope_tree,
1628+
span,
1629+
None,
1630+
generic,
1631+
lower_bound_region,
1632+
)
1633+
.buffer(&mut self.errors_buffer);
1634+
},
1635+
1636+
RegionErrorKind::UnexpectedHiddenRegion {
1637+
opaque_type_def_id, hidden_ty, member_region,
1638+
} => {
1639+
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
1640+
opaque_types::unexpected_hidden_region_diagnostic(
1641+
self.infcx.tcx,
1642+
Some(region_scope_tree),
1643+
opaque_type_def_id,
1644+
hidden_ty,
1645+
member_region,
1646+
)
1647+
.buffer(&mut self.errors_buffer);
1648+
}
1649+
1650+
RegionErrorKind::BoundUniversalRegionError {
1651+
longer_fr, fr_origin, error_region,
1652+
} => {
1653+
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
1654+
let (_, span) = self.nonlexical_regioncx.find_outlives_blame_span(
1655+
&self.body,
1656+
longer_fr,
1657+
fr_origin,
1658+
error_region,
1659+
);
1660+
1661+
// FIXME: improve this error message
1662+
self.infcx
1663+
.tcx
1664+
.sess
1665+
.struct_span_err(span, "higher-ranked subtype error")
1666+
.buffer(&mut self.errors_buffer);
1667+
}
1668+
1669+
RegionErrorKind::RegionError {
1670+
fr_origin, longer_fr, shorter_fr, is_reported,
1671+
} => {
1672+
if is_reported {
1673+
let db = self.nonlexical_regioncx.report_error(
1674+
&self.body,
1675+
&self.local_names,
1676+
&self.upvars,
1677+
self.infcx,
1678+
self.mir_def_id,
1679+
longer_fr,
1680+
fr_origin,
1681+
shorter_fr,
1682+
&mut outlives_suggestion,
1683+
&mut region_naming,
1684+
);
1685+
1686+
db.buffer(&mut self.errors_buffer);
1687+
} else {
1688+
// We only report the first error, so as not to overwhelm the user. See
1689+
// `RegRegionErrorKind` docs.
1690+
//
1691+
// FIXME: currently we do nothing with these, but perhaps we can do better?
1692+
// FIXME: try collecting these constraints on the outlives suggestion
1693+
// builder. Does it make the suggestions any better?
1694+
debug!(
1695+
"Unreported region error: can't prove that {:?}: {:?}",
1696+
longer_fr, shorter_fr
1697+
);
1698+
}
1699+
}
1700+
}
1701+
}
1702+
1703+
// Emit one outlives suggestions for each MIR def we borrowck
1704+
outlives_suggestion.add_suggestion(
1705+
&self.body,
1706+
&self.nonlexical_regioncx,
1707+
self.infcx,
1708+
&mut self.errors_buffer,
1709+
&mut region_naming
1710+
);
1711+
}
15631712
}
15641713

15651714
impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {

0 commit comments

Comments
 (0)