dump out causal information for "free region" errors

The result is not especially illuminating, but that's ok.
This commit is contained in:
Niko Matsakis 2017-12-07 04:30:15 -05:00
parent 39b0e49ebd
commit 594c386549
3 changed files with 111 additions and 9 deletions

View file

@ -1190,6 +1190,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"choose which RELRO level to use"), "choose which RELRO level to use"),
nll: bool = (false, parse_bool, [UNTRACKED], nll: bool = (false, parse_bool, [UNTRACKED],
"run the non-lexical lifetimes MIR pass"), "run the non-lexical lifetimes MIR pass"),
nll_dump_cause: bool = (false, parse_bool, [UNTRACKED],
"dump cause information when reporting errors from NLL"),
trans_time_graph: bool = (false, parse_bool, [UNTRACKED], trans_time_graph: bool = (false, parse_bool, [UNTRACKED],
"generate a graphical HTML report of time spent in trans and LLVM"), "generate a graphical HTML report of time spent in trans and LLVM"),
thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],

View file

@ -21,6 +21,7 @@ use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegi
use rustc::traits::ObligationCause; use rustc::traits::ObligationCause;
use rustc::ty::{self, RegionVid, Ty, TypeFoldable}; use rustc::ty::{self, RegionVid, Ty, TypeFoldable};
use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::DiagnosticBuilder;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use syntax::ast; use syntax::ast;
@ -101,7 +102,7 @@ pub(crate) enum Cause {
LiveOther(Location), LiveOther(Location),
/// part of the initial set of values for a universally quantified region /// part of the initial set of values for a universally quantified region
UniversalRegion, UniversalRegion(RegionVid),
/// Element E was added to R because there was some /// Element E was added to R because there was some
/// outlives obligation `R: R1 @ P` and `R1` contained `E`. /// outlives obligation `R: R1 @ P` and `R1` contained `E`.
@ -300,11 +301,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Add all nodes in the CFG to liveness constraints // Add all nodes in the CFG to liveness constraints
for point_index in self.elements.all_point_indices() { for point_index in self.elements.all_point_indices() {
self.liveness_constraints.add(variable, point_index, &Cause::UniversalRegion); self.liveness_constraints.add(
variable,
point_index,
&Cause::UniversalRegion(variable),
);
} }
// Add `end(X)` into the set for X. // Add `end(X)` into the set for X.
self.liveness_constraints.add(variable, variable, &Cause::UniversalRegion); self.liveness_constraints
.add(variable, variable, &Cause::UniversalRegion(variable));
} }
} }
@ -406,7 +412,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.check_type_tests(infcx, mir, outlives_requirements.as_mut()); self.check_type_tests(infcx, mir, outlives_requirements.as_mut());
self.check_universal_regions(infcx, outlives_requirements.as_mut()); self.check_universal_regions(infcx, mir, outlives_requirements.as_mut());
let outlives_requirements = outlives_requirements.unwrap_or(vec![]); let outlives_requirements = outlives_requirements.unwrap_or(vec![]);
@ -782,6 +788,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
fn check_universal_regions<'gcx>( fn check_universal_regions<'gcx>(
&self, &self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>, infcx: &InferCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>, mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
) { ) {
// The universal regions are always found in a prefix of the // The universal regions are always found in a prefix of the
@ -794,7 +801,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// they did not grow too large, accumulating any requirements // they did not grow too large, accumulating any requirements
// for our caller into the `outlives_requirements` vector. // for our caller into the `outlives_requirements` vector.
for (fr, _) in universal_definitions { for (fr, _) in universal_definitions {
self.check_universal_region(infcx, fr, &mut propagated_outlives_requirements); self.check_universal_region(infcx, mir, fr, &mut propagated_outlives_requirements);
} }
} }
@ -809,6 +816,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
fn check_universal_region<'gcx>( fn check_universal_region<'gcx>(
&self, &self,
infcx: &InferCtxt<'_, 'gcx, 'tcx>, infcx: &InferCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
longer_fr: RegionVid, longer_fr: RegionVid,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>, propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
) { ) {
@ -865,13 +873,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Note: in this case, we use the unapproximated regions // Note: in this case, we use the unapproximated regions
// to report the error. This gives better error messages // to report the error. This gives better error messages
// in some cases. // in some cases.
self.report_error(infcx, longer_fr, shorter_fr, blame_span); self.report_error(infcx, mir, longer_fr, shorter_fr, blame_span);
} }
} }
/// Report an error because the universal region `fr` was required to outlive
/// `outlived_fr` but it is not known to do so. For example:
///
/// ```
/// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
/// ```
///
/// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
fn report_error( fn report_error(
&self, &self,
infcx: &InferCtxt<'_, '_, 'tcx>, infcx: &InferCtxt<'_, '_, 'tcx>,
mir: &Mir<'tcx>,
fr: RegionVid, fr: RegionVid,
outlived_fr: RegionVid, outlived_fr: RegionVid,
blame_span: Span, blame_span: Span,
@ -888,10 +905,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
None => format!("free region `{:?}`", outlived_fr), None => format!("free region `{:?}`", outlived_fr),
}; };
infcx.tcx.sess.span_err( let mut diag = infcx.tcx.sess.struct_span_err(
blame_span, blame_span,
&format!("{} does not outlive {}", fr_string, outlived_fr_string,), &format!("{} does not outlive {}", fr_string, outlived_fr_string,),
); );
// Find out why `fr` had to outlive `outlived_fr`...
let inferred_values = self.inferred_values.as_ref().unwrap();
if let Some(cause) = inferred_values.cause(fr, outlived_fr) {
cause.label_diagnostic(mir, &mut diag);
}
diag.emit();
} }
/// Tries to finds a good span to blame for the fact that `fr1` /// Tries to finds a good span to blame for the fact that `fr1`
@ -1129,3 +1154,61 @@ impl CauseExt for Rc<Cause> {
} }
} }
} }
impl Cause {
pub(crate) fn label_diagnostic(&self, mir: &Mir<'_>, diag: &mut DiagnosticBuilder<'_>) {
// The cause information is pretty messy. Only dump it as an
// internal debugging aid if -Znll-dump-cause is given.
let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.nll_dump_cause);
if !nll_dump_cause {
return;
}
let mut string = String::new();
self.push_diagnostic_string(mir, &mut string);
diag.note(&string);
}
fn push_diagnostic_string(&self, mir: &Mir<'_>, string: &mut String) {
match self {
Cause::LiveVar(local, location) => {
string.push_str(&format!("because `{:?}` is live at {:?}", local, location));
}
Cause::DropVar(local, location) => {
string.push_str(&format!(
"because `{:?}` is dropped at {:?}",
local,
location
));
}
Cause::LiveOther(location) => {
string.push_str(&format!(
"because of a general liveness constraint at {:?}",
location
));
}
Cause::UniversalRegion(region_vid) => {
string.push_str(&format!(
"because `{:?}` is universally quantified",
region_vid
));
}
Cause::Outlives {
original_cause,
constraint_location,
constraint_span: _,
} => {
string.push_str(&format!(
"because of an outlives relation created at `{:?}`\n",
constraint_location
));
original_cause.push_diagnostic_string(mir, string);
}
}
}
}

View file

@ -261,8 +261,11 @@ impl RegionValues {
} }
} }
/// Adds all the universal regions outlived by `from_region` to /// Adds `elem` to `to_region` because of a relation:
/// `to_region`. ///
/// to_region: from_region @ constraint_location
///
/// that was added by the cod at `constraint_span`.
pub(super) fn add_due_to_outlives<T: ToElementIndex>( pub(super) fn add_due_to_outlives<T: ToElementIndex>(
&mut self, &mut self,
from_region: RegionVid, from_region: RegionVid,
@ -418,4 +421,18 @@ impl RegionValues {
)); ));
} }
} }
/// Given a region `r` that contains the element `elem`, returns the `Cause`
/// that tells us *why* `elem` is found in that region.
///
/// Returns None if cause tracking is disabled or `elem` is not
/// actually found in `r`.
pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Rc<Cause>> {
let index = self.elements.index(elem);
if let Some(causes) = &self.causes {
causes.get(&(r, index)).cloned()
} else {
None
}
}
} }