1
Fork 0

Auto merge of #53088 - matthewjasper:closure-region-spans, r=nikomatsakis

[NLL] Use span of the closure args in free region errors

Also adds a note when one of the free regions is BrEnv.
In a future PR I'll change these messages to say "return requires", which should improve them a bit more.

r? @nikomatsakis
This commit is contained in:
bors 2018-08-08 12:48:32 +00:00
commit 63fe441db8
6 changed files with 112 additions and 63 deletions

View file

@ -10,6 +10,7 @@
use borrow_check::nll::region_infer::RegionInferenceContext; use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid; use borrow_check::nll::ToRegionVid;
use borrow_check::nll::universal_regions::DefiningTy;
use rustc::hir; use rustc::hir;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt; use rustc::infer::InferCtxt;
@ -72,7 +73,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}) })
.or_else(|| { .or_else(|| {
self.give_name_if_anonymous_region_appears_in_output( self.give_name_if_anonymous_region_appears_in_output(
infcx.tcx, mir, fr, counter, diag) infcx, mir, mir_def_id, fr, counter, diag)
}) })
.unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr)) .unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr))
} }
@ -107,13 +108,46 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}, },
ty::BoundRegion::BrEnv => { ty::BoundRegion::BrEnv => {
let closure_span = tcx.hir.span_if_local(mir_def_id).unwrap(); let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir");
let region_name = self.synthesize_region_name(counter); let def_ty = self.universal_regions.defining_ty;
diag.span_label(
closure_span, if let DefiningTy::Closure(def_id, substs) = def_ty {
format!("lifetime `{}` represents the closure body", region_name), let args_span = if let hir::ExprKind::Closure(_, _, _, span, _)
); = tcx.hir.expect_expr(mir_node_id).node
Some(region_name) {
span
} else {
bug!("Closure is not defined by a closure expr");
};
let region_name = self.synthesize_region_name(counter);
diag.span_label(
args_span,
format!("lifetime `{}` represents this closure's body", region_name),
);
let closure_kind_ty = substs.closure_kind_ty(def_id, tcx);
let note = match closure_kind_ty.to_opt_closure_kind() {
Some(ty::ClosureKind::Fn) => {
"closure implements `Fn`, so references to captured variables \
can't escape the closure"
}
Some(ty::ClosureKind::FnMut) => {
"closure implements `FnMut`, so references to captured variables \
can't escape the closure"
}
Some(ty::ClosureKind::FnOnce) => {
bug!("BrEnv in a `FnOnce` closure");
}
None => bug!("Closure kind not inferred in borrow check"),
};
diag.note(note);
Some(region_name)
} else {
// Can't have BrEnv in functions, constants or generators.
bug!("BrEnv outside of closure.");
}
} }
ty::BoundRegion::BrAnon(_) | ty::BoundRegion::BrFresh(_) => None, ty::BoundRegion::BrAnon(_) | ty::BoundRegion::BrFresh(_) => None,
@ -543,28 +577,51 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// or be early bound (named, not in argument). /// or be early bound (named, not in argument).
fn give_name_if_anonymous_region_appears_in_output( fn give_name_if_anonymous_region_appears_in_output(
&self, &self,
tcx: TyCtxt<'_, '_, 'tcx>, infcx: &InferCtxt<'_, '_, 'tcx>,
mir: &Mir<'tcx>, mir: &Mir<'tcx>,
mir_def_id: DefId,
fr: RegionVid, fr: RegionVid,
counter: &mut usize, counter: &mut usize,
diag: &mut DiagnosticBuilder<'_>, diag: &mut DiagnosticBuilder<'_>,
) -> Option<InternedString> { ) -> Option<InternedString> {
let tcx = infcx.tcx;
let return_ty = self.universal_regions.unnormalized_output_ty; let return_ty = self.universal_regions.unnormalized_output_ty;
debug!( debug!(
"give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", "give_name_if_anonymous_region_appears_in_output: return_ty = {:?}",
return_ty return_ty
); );
if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) { if !infcx.tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
return None; return None;
} }
let region_name = self.synthesize_region_name(counter); let type_name = with_highlight_region(fr, *counter, || {
infcx.extract_type_name(&return_ty)
});
let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir");
let (return_span, mir_description) = if let hir::ExprKind::Closure(_, _, _, span, gen_move)
= tcx.hir.expect_expr(mir_node_id).node
{
(
tcx.sess.codemap().end_point(span),
if gen_move.is_some() { " of generator" } else { " of closure" }
)
} else {
// unreachable?
(mir.span, "")
};
diag.span_label( diag.span_label(
mir.span, return_span,
format!("lifetime `{}` appears in return type", region_name), format!("return type{} is {}", mir_description, type_name),
); );
Some(region_name) // This counter value will already have been used, so this function will increment it
// so the next value will be used next and return the region name that would have been
// used.
Some(self.synthesize_region_name(counter))
} }
/// Create a synthetic region named `'1`, incrementing the /// Create a synthetic region named `'1`, incrementing the

View file

@ -8,11 +8,10 @@ error: unsatisfied lifetime constraints
--> $DIR/E0621-does-not-trigger-for-closures.rs:25:26 --> $DIR/E0621-does-not-trigger-for-closures.rs:25:26
| |
LL | invoke(&x, |a, b| if a > b { a } else { b }); //~ ERROR E0495 LL | invoke(&x, |a, b| if a > b { a } else { b }); //~ ERROR E0495
| ----------^^^^^----------------- | -- ^^^^^ requires that `'1` must outlive `'2`
| | | | | ||
| | | requires that `'1` must outlive `'2` | |return type of closure is &'2 i32
| | has type `&'1 i32` | has type `&'1 i32`
| lifetime `'2` appears in return type
error: aborting due to previous error error: aborting due to previous error

View file

@ -1,17 +1,15 @@
error: unsatisfied lifetime constraints error: unsatisfied lifetime constraints
--> $DIR/issue-40510-1.rs:18:9 --> $DIR/issue-40510-1.rs:18:9
| |
LL | || { LL | || {
| _____- | --
| |_____| | ||
| || | |return type of closure is &'2 mut std::boxed::Box<()>
LL | || &mut x | lifetime `'1` represents this closure's body
| || ^^^^^^ return requires that `'1` must outlive `'2` LL | &mut x
LL | || }; | ^^^^^^ return requires that `'1` must outlive `'2`
| || - |
| ||_____| = note: closure implements `FnMut`, so references to captured variables can't escape the closure
| |______lifetime `'1` represents the closure body
| lifetime `'2` appears in return type
error: aborting due to previous error error: aborting due to previous error

View file

@ -1,20 +1,17 @@
error: unsatisfied lifetime constraints error: unsatisfied lifetime constraints
--> $DIR/issue-40510-3.rs:18:9 --> $DIR/issue-40510-3.rs:18:9
| |
LL | || { LL | || {
| _____- | --
| |_____| | ||
| || | |return type of closure is [closure@$DIR/issue-40510-3.rs:18:9: 20:10 x:&'2 mut std::vec::Vec<()>]
LL | || || { | lifetime `'1` represents this closure's body
| ||_________^ LL | / || {
LL | ||| x.push(()) LL | | x.push(())
LL | ||| } LL | | }
| |||_________^ requires that `'1` must outlive `'2` | |_________^ requires that `'1` must outlive `'2`
LL | || }; |
| || - = note: closure implements `FnMut`, so references to captured variables can't escape the closure
| ||_____|
| |______lifetime `'1` represents the closure body
| lifetime `'2` appears in return type
error: aborting due to previous error error: aborting due to previous error

View file

@ -1,20 +1,17 @@
error: unsatisfied lifetime constraints error: unsatisfied lifetime constraints
--> $DIR/issue-49824.rs:22:9 --> $DIR/issue-49824.rs:22:9
| |
LL | || { LL | || {
| _____- | --
| |_____| | ||
| || | |return type of closure is [closure@$DIR/issue-49824.rs:22:9: 24:10 x:&'2 mut i32]
LL | || || { | lifetime `'1` represents this closure's body
| ||_________^ LL | / || {
LL | ||| let _y = &mut x; LL | | let _y = &mut x;
LL | ||| } LL | | }
| |||_________^ requires that `'1` must outlive `'2` | |_________^ requires that `'1` must outlive `'2`
LL | || }; |
| || - = note: closure implements `FnMut`, so references to captured variables can't escape the closure
| ||_____|
| |______lifetime `'1` represents the closure body
| lifetime `'2` appears in return type
error: aborting due to previous error error: aborting due to previous error

View file

@ -2,11 +2,12 @@ error: unsatisfied lifetime constraints
--> $DIR/issue-48238.rs:21:13 --> $DIR/issue-48238.rs:21:13
| |
LL | move || use_val(&orig); //~ ERROR LL | move || use_val(&orig); //~ ERROR
| --------^^^^^^^^^^^^^^ | ------- ^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
| | | | | |
| | argument requires that `'1` must outlive `'2` | | return type of closure is &'2 u8
| lifetime `'1` represents the closure body | lifetime `'1` represents this closure's body
| lifetime `'2` appears in return type |
= note: closure implements `Fn`, so references to captured variables can't escape the closure
error: aborting due to previous error error: aborting due to previous error