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:
commit
63fe441db8
6 changed files with 112 additions and 63 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue