Auto merge of #67241 - mark-i-m:simplify-borrow_check-3, r=matthewjasper

Refactorings to borrowck region diagnostic reporting

This PR is a followup to #66886 and #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 https://github.com/rust-lang/rust/pull/66886#issuecomment-559949499
- 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.
This commit is contained in:
bors 2019-12-24 18:02:10 +00:00
commit a9c1c04e98
7 changed files with 344 additions and 289 deletions

View File

@ -32,7 +32,7 @@
crate use mutability_errors::AccessKind;
crate use outlives_suggestion::OutlivesSuggestionBuilder;
crate use region_errors::{ErrorConstraintInfo, ErrorReportingCtx};
crate use region_errors::{ErrorConstraintInfo, ErrorReportingCtx, RegionErrorKind, RegionErrors};
crate use region_name::{RegionErrorNamingCtx, RegionName, RegionNameSource};
pub(super) struct IncludingDowncast(pub(super) bool);

View File

@ -243,7 +243,9 @@ fn compile_all_suggestions<'tcx>(
// If there is only one constraint to suggest, then we already suggested it in the
// intermediate suggestion above.
if self.constraints_to_add.len() == 1 {
if self.constraints_to_add.len() == 1
&& self.constraints_to_add.values().next().unwrap().len() == 1
{
debug!("Only 1 suggestion. Skipping.");
return;
}

View File

@ -2,10 +2,11 @@
use rustc::hir::def_id::DefId;
use rustc::infer::{
error_reporting::nice_region_error::NiceRegionError, InferCtxt, NLLRegionVariableOrigin,
error_reporting::nice_region_error::NiceRegionError, region_constraints::GenericKind,
InferCtxt, NLLRegionVariableOrigin,
};
use rustc::mir::{Body, ConstraintCategory, Local, Location};
use rustc::ty::{self, RegionVid};
use rustc::ty::{self, RegionVid, Ty};
use rustc_errors::DiagnosticBuilder;
use rustc_index::vec::IndexVec;
use std::collections::VecDeque;
@ -47,13 +48,74 @@ fn description(&self) -> &'static str {
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Trace {
StartRegion,
FromOutlivesConstraint(OutlivesConstraint),
NotVisited,
}
/// A collection of errors encountered during region inference. This is needed to efficiently
/// report errors after borrow checking.
///
/// Usually we expect this to either be empty or contain a small number of items, so we can avoid
/// allocation most of the time.
crate type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>;
#[derive(Clone, Debug)]
crate enum RegionErrorKind<'tcx> {
/// An error for a type test: `T: 'a` does not live long enough.
TypeTestDoesNotLiveLongEnough {
/// The span of the type test.
span: Span,
/// The generic type of the type test.
generic: GenericKind<'tcx>,
},
/// A generic bound failure for a type test.
TypeTestGenericBoundError {
/// The span of the type test.
span: Span,
/// The generic type of the type test.
generic: GenericKind<'tcx>,
/// The lower bound region.
lower_bound_region: ty::Region<'tcx>,
},
/// An unexpected hidden region for an opaque type.
UnexpectedHiddenRegion {
/// The def id of the opaque type.
opaque_type_def_id: DefId,
/// The hidden type.
hidden_ty: Ty<'tcx>,
/// The unexpected region.
member_region: ty::Region<'tcx>,
},
/// Higher-ranked subtyping error.
BoundUniversalRegionError {
/// The placeholder free region.
longer_fr: RegionVid,
/// The region that erroneously must be outlived by `longer_fr`.
error_region: RegionVid,
/// The origin of the placeholder region.
fr_origin: NLLRegionVariableOrigin,
},
/// Any other lifetime error.
RegionError {
/// The origin of the region.
fr_origin: NLLRegionVariableOrigin,
/// The region that should outlive `shorter_fr`.
longer_fr: RegionVid,
/// The region that should be shorter, but we can't prove it.
shorter_fr: RegionVid,
/// Indicates whether this is a reported error. We currently only report the first error
/// encountered and leave the rest unreported so as not to overwhelm the user.
is_reported: bool,
},
}
/// Various pieces of state used when reporting borrow checker errors.
pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
/// The region inference context used for borrow chekcing this MIR body.

View File

@ -1,9 +1,7 @@
//! This query borrow-checks the MIR to (further) ensure it is not broken.
use rustc::hir::def_id::DefId;
use rustc::hir::Node;
use rustc::hir::{self, HirId};
use rustc::infer::InferCtxt;
use rustc::hir::{self, def_id::DefId, HirId, Node};
use rustc::infer::{opaque_types, InferCtxt};
use rustc::lint::builtin::MUTABLE_BORROW_RESERVATION_CONFLICT;
use rustc::lint::builtin::UNUSED_MUT;
use rustc::mir::{
@ -39,8 +37,11 @@
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::{do_dataflow, DebugFormatted};
use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use crate::transform::MirSource;
use self::diagnostics::AccessKind;
use self::diagnostics::{
AccessKind, OutlivesSuggestionBuilder, RegionErrorKind, RegionErrorNamingCtx, RegionErrors,
};
use self::flows::Flows;
use self::location::LocationTable;
use self::prefixes::PrefixSet;
@ -202,22 +203,28 @@ fn do_mir_borrowck<'a, 'tcx>(
let borrow_set =
Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data));
// If we are in non-lexical mode, compute the non-lexical lifetimes.
let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions(
infcx,
def_id,
free_regions,
body,
&promoted,
&local_names,
&upvars,
location_table,
param_env,
&mut flow_inits,
&mdpe.move_data,
&borrow_set,
&mut errors_buffer,
);
// Compute non-lexical lifetimes.
let nll::NllOutput { regioncx, polonius_output, opt_closure_req, nll_errors } =
nll::compute_regions(
infcx,
def_id,
free_regions,
body,
&promoted,
location_table,
param_env,
&mut flow_inits,
&mdpe.move_data,
&borrow_set,
);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests, as well as helping with debugging.
nll::dump_mir_results(infcx, MirSource::item(def_id), &body, &regioncx, &opt_closure_req);
// We also have a `#[rustc_nll]` annotation that causes us to dump
// information.
nll::dump_annotation(infcx, &body, def_id, &regioncx, &opt_closure_req, &mut errors_buffer);
// The various `flow_*` structures can be large. We drop `flow_inits` here
// so it doesn't overlap with the others below. This reduces peak memory
@ -288,6 +295,9 @@ fn do_mir_borrowck<'a, 'tcx>(
local_names,
};
// Compute and report region errors, if any.
mbcx.report_region_errors(nll_errors);
let mut state = Flows::new(flow_borrows, flow_uninits, flow_ever_inits, polonius_output);
if let Some(errors) = move_errors {
@ -1464,6 +1474,131 @@ fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flo
// initial reservation.
}
}
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
// buffered in the `MirBorrowckCtxt`.
// FIXME(mark-i-m): Would be great to get rid of the naming context.
let mut region_naming = RegionErrorNamingCtx::new();
let mut outlives_suggestion =
OutlivesSuggestionBuilder::new(self.mir_def_id, &self.local_names);
for nll_error in nll_errors.into_iter() {
match nll_error {
RegionErrorKind::TypeTestDoesNotLiveLongEnough { span, generic } => {
// FIXME. We should handle this case better. It
// indicates that we have e.g., some region variable
// whose value is like `'a+'b` where `'a` and `'b` are
// distinct unrelated univesal regions that are not
// known to outlive one another. It'd be nice to have
// some examples where this arises to decide how best
// to report it; we could probably handle it by
// iterating over the universal regions and reporting
// an error that multiple bounds are required.
self.infcx
.tcx
.sess
.struct_span_err(span, &format!("`{}` does not live long enough", generic))
.buffer(&mut self.errors_buffer);
}
RegionErrorKind::TypeTestGenericBoundError {
span,
generic,
lower_bound_region,
} => {
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
self.infcx
.construct_generic_bound_failure(
region_scope_tree,
span,
None,
generic,
lower_bound_region,
)
.buffer(&mut self.errors_buffer);
}
RegionErrorKind::UnexpectedHiddenRegion {
opaque_type_def_id,
hidden_ty,
member_region,
} => {
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
opaque_types::unexpected_hidden_region_diagnostic(
self.infcx.tcx,
Some(region_scope_tree),
opaque_type_def_id,
hidden_ty,
member_region,
)
.buffer(&mut self.errors_buffer);
}
RegionErrorKind::BoundUniversalRegionError {
longer_fr,
fr_origin,
error_region,
} => {
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
let (_, span) = self.nonlexical_regioncx.find_outlives_blame_span(
&self.body,
longer_fr,
fr_origin,
error_region,
);
// FIXME: improve this error message
self.infcx
.tcx
.sess
.struct_span_err(span, "higher-ranked subtype error")
.buffer(&mut self.errors_buffer);
}
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
if is_reported {
let db = self.nonlexical_regioncx.report_error(
&self.body,
&self.local_names,
&self.upvars,
self.infcx,
self.mir_def_id,
longer_fr,
fr_origin,
shorter_fr,
&mut outlives_suggestion,
&mut region_naming,
);
db.buffer(&mut self.errors_buffer);
} else {
// We only report the first error, so as not to overwhelm the user. See
// `RegRegionErrorKind` docs.
//
// FIXME: currently we do nothing with these, but perhaps we can do better?
// FIXME: try collecting these constraints on the outlives suggestion
// builder. Does it make the suggestions any better?
debug!(
"Unreported region error: can't prove that {:?}: {:?}",
longer_fr, shorter_fr
);
}
}
}
}
// Emit one outlives suggestions for each MIR def we borrowck
outlives_suggestion.add_suggestion(
&self.body,
&self.nonlexical_regioncx,
self.infcx,
&mut self.errors_buffer,
&mut region_naming,
);
}
}
impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {

View File

@ -3,8 +3,8 @@
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::mir::{
BasicBlock, Body, BodyAndCache, ClosureOutlivesSubject, ClosureRegionRequirements, Local,
LocalKind, Location, Promoted, ReadOnlyBodyAndCache,
BasicBlock, Body, BodyAndCache, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind,
Location, Promoted, ReadOnlyBodyAndCache,
};
use rustc::ty::{self, RegionKind, RegionVid};
use rustc_errors::Diagnostic;
@ -16,7 +16,6 @@
use std::rc::Rc;
use std::str::FromStr;
use syntax::symbol::sym;
use syntax_pos::symbol::Symbol;
use self::mir_util::PassWhere;
use polonius_engine::{Algorithm, Output};
@ -31,6 +30,7 @@
use crate::borrow_check::{
borrow_set::BorrowSet,
constraint_generation,
diagnostics::RegionErrors,
facts::{AllFacts, AllFactsExt, RustcFacts},
invalidation,
location::LocationTable,
@ -38,14 +38,21 @@
renumber,
type_check::{self, MirTypeckRegionConstraints, MirTypeckResults},
universal_regions::UniversalRegions,
Upvar,
};
crate type PoloniusOutput = Output<RustcFacts>;
/// Rewrites the regions in the MIR to use NLL variables, also
/// scraping out the set of universal regions (e.g., region parameters)
/// declared on the function. That set will need to be given to
/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
/// closure requirements to propagate, and any generated errors.
crate struct NllOutput<'tcx> {
pub regioncx: RegionInferenceContext<'tcx>,
pub polonius_output: Option<Rc<PoloniusOutput>>,
pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
pub nll_errors: RegionErrors<'tcx>,
}
/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
/// regions (e.g., region parameters) declared on the function. That set will need to be given to
/// `compute_regions`.
pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>(
infcx: &InferCtxt<'cx, 'tcx>,
@ -140,19 +147,12 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
universal_regions: UniversalRegions<'tcx>,
body: ReadOnlyBodyAndCache<'_, 'tcx>,
promoted: &IndexVec<Promoted, ReadOnlyBodyAndCache<'_, 'tcx>>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
location_table: &LocationTable,
param_env: ty::ParamEnv<'tcx>,
flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>,
errors_buffer: &mut Vec<Diagnostic>,
) -> (
RegionInferenceContext<'tcx>,
Option<Rc<PoloniusOutput>>,
Option<ClosureRegionRequirements<'tcx>>,
) {
) -> NllOutput<'tcx> {
let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default());
let universal_regions = Rc::new(universal_regions);
@ -284,34 +284,18 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
});
// Solve the region constraints.
let closure_region_requirements = regioncx.solve(
infcx,
&body,
local_names,
upvars,
def_id,
errors_buffer,
polonius_output.clone(),
);
let (closure_region_requirements, nll_errors) =
regioncx.solve(infcx, &body, def_id, polonius_output.clone());
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests, as well as helping with debugging.
dump_mir_results(
infcx,
MirSource::item(def_id),
&body,
&regioncx,
&closure_region_requirements,
);
// We also have a `#[rustc_nll]` annotation that causes us to dump
// information
dump_annotation(infcx, &body, def_id, &regioncx, &closure_region_requirements, errors_buffer);
(regioncx, polonius_output, closure_region_requirements)
NllOutput {
regioncx,
polonius_output,
opt_closure_req: closure_region_requirements,
nll_errors,
}
}
fn dump_mir_results<'a, 'tcx>(
pub(super) fn dump_mir_results<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
source: MirSource<'tcx>,
body: &Body<'tcx>,
@ -362,7 +346,7 @@ fn dump_mir_results<'a, 'tcx>(
};
}
fn dump_annotation<'a, 'tcx>(
pub(super) fn dump_annotation<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
body: &Body<'tcx>,
mir_def_id: DefId,

View File

@ -2,7 +2,6 @@
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryOutlivesConstraint;
use rustc::infer::opaque_types;
use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin};
use rustc::mir::{
@ -10,23 +9,20 @@
ConstraintCategory, Local, Location,
};
use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable};
use rustc::util::common::ErrorReported;
use rustc_data_structures::binary_search_util;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::graph::vec_graph::VecGraph;
use rustc_data_structures::graph::WithSuccessors;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use syntax_pos::symbol::Symbol;
use syntax_pos::Span;
use crate::borrow_check::{
constraints::{
graph::NormalConstraintGraph, ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
},
diagnostics::{OutlivesSuggestionBuilder, RegionErrorNamingCtx},
diagnostics::{RegionErrorKind, RegionErrors},
member_constraints::{MemberConstraintSet, NllMemberConstraintIndex},
nll::{PoloniusOutput, ToRegionVid},
region_infer::values::{
@ -35,7 +31,6 @@
},
type_check::{free_region_relations::UniversalRegionRelations, Locations},
universal_regions::UniversalRegions,
Upvar,
};
mod dump_mir;
@ -221,6 +216,15 @@ pub struct TypeTest<'tcx> {
pub verify_bound: VerifyBound<'tcx>,
}
/// When we have an unmet lifetime constraint, we try to propagate it outward (e.g. to a closure
/// environment). If we can't, it is an error.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum RegionRelationCheckResult {
Ok,
Propagated,
Error,
}
impl<'tcx> RegionInferenceContext<'tcx> {
/// Creates a new region inference context with a total of
/// `num_region_variables` valid inference variables; the first N
@ -423,7 +427,7 @@ pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
}
/// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`.
crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>) {
crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut rustc_errors::DiagnosticBuilder<'_>) {
self.universal_regions.annotate(tcx, err)
}
@ -469,14 +473,13 @@ pub(super) fn solve(
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
errors_buffer: &mut Vec<Diagnostic>,
polonius_output: Option<Rc<PoloniusOutput>>,
) -> Option<ClosureRegionRequirements<'tcx>> {
) -> (Option<ClosureRegionRequirements<'tcx>>, RegionErrors<'tcx>) {
self.propagate_constraints(body);
let mut errors_buffer = RegionErrors::new();
// If this is a closure, we can propagate unsatisfied
// `outlives_requirements` to our creator, so create a vector
// to store those. Otherwise, we'll pass in `None` to the
@ -484,57 +487,34 @@ pub(super) fn solve(
// eagerly.
let mut outlives_requirements = infcx.tcx.is_closure(mir_def_id).then(|| vec![]);
self.check_type_tests(
infcx,
body,
mir_def_id,
outlives_requirements.as_mut(),
errors_buffer,
);
// If we produce any errors, we keep track of the names of all regions, so that we can use
// the same error names in any suggestions we produce. Note that we need names to be unique
// across different errors for the same MIR def so that we can make suggestions that fix
// multiple problems.
let mut region_naming = RegionErrorNamingCtx::new();
self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer);
// In Polonius mode, the errors about missing universal region relations are in the output
// and need to be emitted or propagated. Otherwise, we need to check whether the
// constraints were too strong, and if so, emit or propagate those errors.
if infcx.tcx.sess.opts.debugging_opts.polonius {
self.check_polonius_subset_errors(
infcx,
body,
local_names,
upvars,
mir_def_id,
outlives_requirements.as_mut(),
errors_buffer,
&mut region_naming,
&mut errors_buffer,
polonius_output.expect("Polonius output is unavailable despite `-Z polonius`"),
);
} else {
self.check_universal_regions(
infcx,
body,
local_names,
upvars,
mir_def_id,
outlives_requirements.as_mut(),
errors_buffer,
&mut region_naming,
);
self.check_universal_regions(body, outlives_requirements.as_mut(), &mut errors_buffer);
}
self.check_member_constraints(infcx, mir_def_id, errors_buffer);
self.check_member_constraints(infcx, &mut errors_buffer);
let outlives_requirements = outlives_requirements.unwrap_or(vec![]);
if outlives_requirements.is_empty() {
None
(None, errors_buffer)
} else {
let num_external_vids = self.universal_regions.num_global_and_external_regions();
Some(ClosureRegionRequirements { num_external_vids, outlives_requirements })
(
Some(ClosureRegionRequirements { num_external_vids, outlives_requirements }),
errors_buffer,
)
}
}
@ -822,9 +802,8 @@ fn check_type_tests(
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
mir_def_id: DefId,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
errors_buffer: &mut RegionErrors<'tcx>,
) {
let tcx = infcx.tcx;
@ -882,32 +861,16 @@ fn check_type_tests(
}
if let Some(lower_bound_region) = lower_bound_region {
let region_scope_tree = &tcx.region_scope_tree(mir_def_id);
infcx
.construct_generic_bound_failure(
region_scope_tree,
type_test_span,
None,
type_test.generic_kind,
lower_bound_region,
)
.buffer(errors_buffer);
errors_buffer.push(RegionErrorKind::TypeTestGenericBoundError {
span: type_test_span,
generic: type_test.generic_kind,
lower_bound_region,
});
} else {
// FIXME. We should handle this case better. It
// indicates that we have e.g., some region variable
// whose value is like `'a+'b` where `'a` and `'b` are
// distinct unrelated univesal regions that are not
// known to outlive one another. It'd be nice to have
// some examples where this arises to decide how best
// to report it; we could probably handle it by
// iterating over the universal regions and reporting
// an error that multiple bounds are required.
tcx.sess
.struct_span_err(
type_test_span,
&format!("`{}` does not live long enough", type_test.generic_kind,),
)
.buffer(errors_buffer);
errors_buffer.push(RegionErrorKind::TypeTestDoesNotLiveLongEnough {
span: type_test_span,
generic: type_test.generic_kind,
});
}
}
}
@ -1300,17 +1263,10 @@ fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
/// report them as errors.
fn check_universal_regions(
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
errors_buffer: &mut RegionErrors<'tcx>,
) {
let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id, local_names);
for (fr, fr_definition) in self.definitions.iter_enumerated() {
match fr_definition.origin {
NLLRegionVariableOrigin::FreeRegion => {
@ -1318,21 +1274,15 @@ fn check_universal_regions(
// they did not grow too large, accumulating any requirements
// for our caller into the `outlives_requirements` vector.
self.check_universal_region(
infcx,
body,
local_names,
upvars,
mir_def_id,
fr,
&mut propagated_outlives_requirements,
&mut outlives_suggestion,
errors_buffer,
region_naming,
);
}
NLLRegionVariableOrigin::Placeholder(placeholder) => {
self.check_bound_universal_region(infcx, body, mir_def_id, fr, placeholder);
self.check_bound_universal_region(fr, placeholder, errors_buffer);
}
NLLRegionVariableOrigin::Existential { .. } => {
@ -1340,9 +1290,6 @@ fn check_universal_regions(
}
}
}
// Emit outlives suggestions
outlives_suggestion.add_suggestion(body, self, infcx, errors_buffer, region_naming);
}
/// Checks if Polonius has found any unexpected free region relations.
@ -1368,14 +1315,9 @@ fn check_universal_regions(
/// report them as errors.
fn check_polonius_subset_errors(
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
errors_buffer: &mut RegionErrors<'tcx>,
polonius_output: Rc<PoloniusOutput>,
) {
debug!(
@ -1383,8 +1325,6 @@ fn check_polonius_subset_errors(
polonius_output.subset_errors.len()
);
let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id, local_names);
// Similarly to `check_universal_regions`: a free region relation, which was not explicitly
// declared ("known") was found by Polonius, so emit an error, or propagate the
// requirements for our caller into the `propagated_outlives_requirements` vector.
@ -1425,27 +1365,13 @@ fn check_polonius_subset_errors(
body,
&mut propagated_outlives_requirements,
);
if !propagated {
// If we are not in a context where we can't propagate errors, or we
// could not shrink `fr` to something smaller, then just report an
// error.
//
// Note: in this case, we use the unapproximated regions to report the
// error. This gives better error messages in some cases.
let db = self.report_error(
body,
local_names,
upvars,
infcx,
mir_def_id,
*longer_fr,
NLLRegionVariableOrigin::FreeRegion,
*shorter_fr,
&mut outlives_suggestion,
region_naming,
);
db.buffer(errors_buffer);
if propagated == RegionRelationCheckResult::Error {
errors_buffer.push(RegionErrorKind::RegionError {
longer_fr: *longer_fr,
shorter_fr: *shorter_fr,
fr_origin: NLLRegionVariableOrigin::FreeRegion,
is_reported: true,
});
}
}
@ -1458,7 +1384,7 @@ fn check_polonius_subset_errors(
}
NLLRegionVariableOrigin::Placeholder(placeholder) => {
self.check_bound_universal_region(infcx, body, mir_def_id, fr, placeholder);
self.check_bound_universal_region(fr, placeholder, errors_buffer);
}
NLLRegionVariableOrigin::Existential { .. } => {
@ -1466,9 +1392,6 @@ fn check_polonius_subset_errors(
}
}
}
// Emit outlives suggestions
outlives_suggestion.add_suggestion(body, self, infcx, errors_buffer, region_naming);
}
/// Checks the final value for the free region `fr` to see if it
@ -1481,16 +1404,10 @@ fn check_polonius_subset_errors(
/// `outlives_requirements` vector.
fn check_universal_region(
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
longer_fr: RegionVid,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
errors_buffer: &mut RegionErrors<'tcx>,
) {
debug!("check_universal_region(fr={:?})", longer_fr);
@ -1509,76 +1426,60 @@ fn check_universal_region(
// one in this SCC, so we will always check the representative here.
let representative = self.scc_representatives[longer_fr_scc];
if representative != longer_fr {
self.check_universal_region_relation(
if let RegionRelationCheckResult::Error = self.check_universal_region_relation(
longer_fr,
representative,
infcx,
body,
local_names,
upvars,
mir_def_id,
propagated_outlives_requirements,
outlives_suggestion,
errors_buffer,
region_naming,
);
) {
errors_buffer.push(RegionErrorKind::RegionError {
longer_fr,
shorter_fr: representative,
fr_origin: NLLRegionVariableOrigin::FreeRegion,
is_reported: true,
});
}
return;
}
// Find every region `o` such that `fr: o`
// (because `fr` includes `end(o)`).
let mut error_reported = false;
for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) {
if let Some(ErrorReported) = self.check_universal_region_relation(
if let RegionRelationCheckResult::Error = self.check_universal_region_relation(
longer_fr,
shorter_fr,
infcx,
body,
local_names,
upvars,
mir_def_id,
propagated_outlives_requirements,
outlives_suggestion,
errors_buffer,
region_naming,
) {
// continuing to iterate just reports more errors than necessary
//
// FIXME It would also allow us to report more Outlives Suggestions, though, so
// it's not clear that that's a bad thing. Somebody should try commenting out this
// line and see it is actually a regression.
return;
// We only report the first region error. Subsequent errors are hidden so as
// not to overwhelm the user, but we do record them so as to potentially print
// better diagnostics elsewhere...
errors_buffer.push(RegionErrorKind::RegionError {
longer_fr,
shorter_fr,
fr_origin: NLLRegionVariableOrigin::FreeRegion,
is_reported: !error_reported,
});
error_reported = true;
}
}
}
/// Checks that we can prove that `longer_fr: shorter_fr`. If we can't we attempt to propagate
/// the constraint outward (e.g. to a closure environment), but if that fails, there is an
/// error.
fn check_universal_region_relation(
&self,
longer_fr: RegionVid,
shorter_fr: RegionVid,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) -> Option<ErrorReported> {
) -> RegionRelationCheckResult {
// If it is known that `fr: o`, carry on.
if self.universal_region_relations.outlives(longer_fr, shorter_fr) {
return None;
}
let propagated = self.try_propagate_universal_region_error(
longer_fr,
shorter_fr,
body,
propagated_outlives_requirements,
);
if propagated {
None
RegionRelationCheckResult::Ok
} else {
// If we are not in a context where we can't propagate errors, or we
// could not shrink `fr` to something smaller, then just report an
@ -1586,36 +1487,24 @@ fn check_universal_region_relation(
//
// Note: in this case, we use the unapproximated regions to report the
// error. This gives better error messages in some cases.
let db = self.report_error(
body,
local_names,
upvars,
infcx,
mir_def_id,
self.try_propagate_universal_region_error(
longer_fr,
NLLRegionVariableOrigin::FreeRegion,
shorter_fr,
outlives_suggestion,
region_naming,
);
db.buffer(errors_buffer);
Some(ErrorReported)
body,
propagated_outlives_requirements,
)
}
}
/// Attempt to propagate a region error (e.g. `'a: 'b`) that is not met to a closure's
/// creator. If we cannot, then the caller should report an error to the user.
///
/// Returns `true` if the error was propagated, and `false` otherwise.
fn try_propagate_universal_region_error(
&self,
longer_fr: RegionVid,
shorter_fr: RegionVid,
body: &Body<'tcx>,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
) -> bool {
) -> RegionRelationCheckResult {
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
// Shrink `longer_fr` until we find a non-local region (if we do).
// We'll call it `fr-` -- it's ever so slightly smaller than
@ -1649,20 +1538,18 @@ fn try_propagate_universal_region_error(
category: blame_span_category.0,
});
}
return true;
return RegionRelationCheckResult::Propagated;
}
}
false
RegionRelationCheckResult::Error
}
fn check_bound_universal_region(
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
_mir_def_id: DefId,
longer_fr: RegionVid,
placeholder: ty::PlaceholderRegion,
errors_buffer: &mut RegionErrors<'tcx>,
) {
debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,);
@ -1699,28 +1586,17 @@ fn check_bound_universal_region(
.unwrap(),
};
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
let (_, span) = self.find_outlives_blame_span(
body,
errors_buffer.push(RegionErrorKind::BoundUniversalRegionError {
longer_fr,
NLLRegionVariableOrigin::Placeholder(placeholder),
error_region,
);
// Obviously, this error message is far from satisfactory.
// At present, though, it only appears in unit tests --
// the AST-based checker uses a more conservative check,
// so to even see this error, one must pass in a special
// flag.
let mut diag = infcx.tcx.sess.struct_span_err(span, "higher-ranked subtype error");
diag.emit();
fr_origin: NLLRegionVariableOrigin::Placeholder(placeholder),
});
}
fn check_member_constraints(
&self,
infcx: &InferCtxt<'_, 'tcx>,
mir_def_id: DefId,
errors_buffer: &mut Vec<Diagnostic>,
errors_buffer: &mut RegionErrors<'tcx>,
) {
let member_constraints = self.member_constraints.clone();
for m_c_i in member_constraints.all_indices() {
@ -1744,16 +1620,12 @@ fn check_member_constraints(
}
// If not, report an error.
let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id);
let member_region = infcx.tcx.mk_region(ty::ReVar(member_region_vid));
opaque_types::unexpected_hidden_region_diagnostic(
infcx.tcx,
Some(region_scope_tree),
m_c.opaque_type_def_id,
m_c.hidden_ty,
errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
opaque_type_def_id: m_c.opaque_type_def_id,
hidden_ty: m_c.hidden_ty,
member_region,
)
.buffer(errors_buffer);
});
}
}
}

View File

@ -44,12 +44,6 @@ LL | | }
|
= help: a `loop` may express intention better if this is on purpose
error: higher-ranked subtype error
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
LL | foo_hrtb_bar_not(&mut t);
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: lifetime may not live long enough
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
@ -61,6 +55,12 @@ LL | foo_hrtb_bar_not(&mut t);
|
= help: consider replacing `'b` with `'static`
error: higher-ranked subtype error
--> $DIR/hrtb-perfect-forwarding.rs:46:5
|
LL | foo_hrtb_bar_not(&mut t);
| ^^^^^^^^^^^^^^^^^^^^^^^^
warning: function cannot return without recursing
--> $DIR/hrtb-perfect-forwarding.rs:49:1
|