Skip to content

Commit 63fe441

Browse files
committed
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
2 parents ebe8df4 + b13e3f8 commit 63fe441

File tree

6 files changed

+112
-63
lines changed

6 files changed

+112
-63
lines changed

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs

+71-14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use borrow_check::nll::region_infer::RegionInferenceContext;
1212
use borrow_check::nll::ToRegionVid;
13+
use borrow_check::nll::universal_regions::DefiningTy;
1314
use rustc::hir;
1415
use rustc::hir::def_id::DefId;
1516
use rustc::infer::InferCtxt;
@@ -72,7 +73,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
7273
})
7374
.or_else(|| {
7475
self.give_name_if_anonymous_region_appears_in_output(
75-
infcx.tcx, mir, fr, counter, diag)
76+
infcx, mir, mir_def_id, fr, counter, diag)
7677
})
7778
.unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr))
7879
}
@@ -107,13 +108,46 @@ impl<'tcx> RegionInferenceContext<'tcx> {
107108
},
108109

109110
ty::BoundRegion::BrEnv => {
110-
let closure_span = tcx.hir.span_if_local(mir_def_id).unwrap();
111-
let region_name = self.synthesize_region_name(counter);
112-
diag.span_label(
113-
closure_span,
114-
format!("lifetime `{}` represents the closure body", region_name),
115-
);
116-
Some(region_name)
111+
let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir");
112+
let def_ty = self.universal_regions.defining_ty;
113+
114+
if let DefiningTy::Closure(def_id, substs) = def_ty {
115+
let args_span = if let hir::ExprKind::Closure(_, _, _, span, _)
116+
= tcx.hir.expect_expr(mir_node_id).node
117+
{
118+
span
119+
} else {
120+
bug!("Closure is not defined by a closure expr");
121+
};
122+
let region_name = self.synthesize_region_name(counter);
123+
diag.span_label(
124+
args_span,
125+
format!("lifetime `{}` represents this closure's body", region_name),
126+
);
127+
128+
let closure_kind_ty = substs.closure_kind_ty(def_id, tcx);
129+
let note = match closure_kind_ty.to_opt_closure_kind() {
130+
Some(ty::ClosureKind::Fn) => {
131+
"closure implements `Fn`, so references to captured variables \
132+
can't escape the closure"
133+
}
134+
Some(ty::ClosureKind::FnMut) => {
135+
"closure implements `FnMut`, so references to captured variables \
136+
can't escape the closure"
137+
}
138+
Some(ty::ClosureKind::FnOnce) => {
139+
bug!("BrEnv in a `FnOnce` closure");
140+
}
141+
None => bug!("Closure kind not inferred in borrow check"),
142+
};
143+
144+
diag.note(note);
145+
146+
Some(region_name)
147+
} else {
148+
// Can't have BrEnv in functions, constants or generators.
149+
bug!("BrEnv outside of closure.");
150+
}
117151
}
118152

119153
ty::BoundRegion::BrAnon(_) | ty::BoundRegion::BrFresh(_) => None,
@@ -543,28 +577,51 @@ impl<'tcx> RegionInferenceContext<'tcx> {
543577
/// or be early bound (named, not in argument).
544578
fn give_name_if_anonymous_region_appears_in_output(
545579
&self,
546-
tcx: TyCtxt<'_, '_, 'tcx>,
580+
infcx: &InferCtxt<'_, '_, 'tcx>,
547581
mir: &Mir<'tcx>,
582+
mir_def_id: DefId,
548583
fr: RegionVid,
549584
counter: &mut usize,
550585
diag: &mut DiagnosticBuilder<'_>,
551586
) -> Option<InternedString> {
587+
let tcx = infcx.tcx;
588+
552589
let return_ty = self.universal_regions.unnormalized_output_ty;
553590
debug!(
554591
"give_name_if_anonymous_region_appears_in_output: return_ty = {:?}",
555592
return_ty
556593
);
557-
if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
594+
if !infcx.tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
558595
return None;
559596
}
560597

561-
let region_name = self.synthesize_region_name(counter);
598+
let type_name = with_highlight_region(fr, *counter, || {
599+
infcx.extract_type_name(&return_ty)
600+
});
601+
602+
let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir");
603+
604+
let (return_span, mir_description) = if let hir::ExprKind::Closure(_, _, _, span, gen_move)
605+
= tcx.hir.expect_expr(mir_node_id).node
606+
{
607+
(
608+
tcx.sess.codemap().end_point(span),
609+
if gen_move.is_some() { " of generator" } else { " of closure" }
610+
)
611+
} else {
612+
// unreachable?
613+
(mir.span, "")
614+
};
615+
562616
diag.span_label(
563-
mir.span,
564-
format!("lifetime `{}` appears in return type", region_name),
617+
return_span,
618+
format!("return type{} is {}", mir_description, type_name),
565619
);
566620

567-
Some(region_name)
621+
// This counter value will already have been used, so this function will increment it
622+
// so the next value will be used next and return the region name that would have been
623+
// used.
624+
Some(self.synthesize_region_name(counter))
568625
}
569626

570627
/// Create a synthetic region named `'1`, incrementing the

src/test/ui/error-codes/E0621-does-not-trigger-for-closures.nll.stderr

+4-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ error: unsatisfied lifetime constraints
88
--> $DIR/E0621-does-not-trigger-for-closures.rs:25:26
99
|
1010
LL | invoke(&x, |a, b| if a > b { a } else { b }); //~ ERROR E0495
11-
| ----------^^^^^-----------------
12-
| | | |
13-
| | | requires that `'1` must outlive `'2`
14-
| | has type `&'1 i32`
15-
| lifetime `'2` appears in return type
11+
| -- ^^^^^ requires that `'1` must outlive `'2`
12+
| ||
13+
| |return type of closure is &'2 i32
14+
| has type `&'1 i32`
1615

1716
error: aborting due to previous error
1817

src/test/ui/issue-40510-1.nll.stderr

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
error: unsatisfied lifetime constraints
22
--> $DIR/issue-40510-1.rs:18:9
33
|
4-
LL | || {
5-
| _____-
6-
| |_____|
7-
| ||
8-
LL | || &mut x
9-
| || ^^^^^^ return requires that `'1` must outlive `'2`
10-
LL | || };
11-
| || -
12-
| ||_____|
13-
| |______lifetime `'1` represents the closure body
14-
| lifetime `'2` appears in return type
4+
LL | || {
5+
| --
6+
| ||
7+
| |return type of closure is &'2 mut std::boxed::Box<()>
8+
| lifetime `'1` represents this closure's body
9+
LL | &mut x
10+
| ^^^^^^ return requires that `'1` must outlive `'2`
11+
|
12+
= note: closure implements `FnMut`, so references to captured variables can't escape the closure
1513

1614
error: aborting due to previous error
1715

src/test/ui/issue-40510-3.nll.stderr

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
error: unsatisfied lifetime constraints
22
--> $DIR/issue-40510-3.rs:18:9
33
|
4-
LL | || {
5-
| _____-
6-
| |_____|
7-
| ||
8-
LL | || || {
9-
| ||_________^
10-
LL | ||| x.push(())
11-
LL | ||| }
12-
| |||_________^ requires that `'1` must outlive `'2`
13-
LL | || };
14-
| || -
15-
| ||_____|
16-
| |______lifetime `'1` represents the closure body
17-
| lifetime `'2` appears in return type
4+
LL | || {
5+
| --
6+
| ||
7+
| |return type of closure is [closure@$DIR/issue-40510-3.rs:18:9: 20:10 x:&'2 mut std::vec::Vec<()>]
8+
| lifetime `'1` represents this closure's body
9+
LL | / || {
10+
LL | | x.push(())
11+
LL | | }
12+
| |_________^ requires that `'1` must outlive `'2`
13+
|
14+
= note: closure implements `FnMut`, so references to captured variables can't escape the closure
1815

1916
error: aborting due to previous error
2017

src/test/ui/issue-49824.nll.stderr

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
error: unsatisfied lifetime constraints
22
--> $DIR/issue-49824.rs:22:9
33
|
4-
LL | || {
5-
| _____-
6-
| |_____|
7-
| ||
8-
LL | || || {
9-
| ||_________^
10-
LL | ||| let _y = &mut x;
11-
LL | ||| }
12-
| |||_________^ requires that `'1` must outlive `'2`
13-
LL | || };
14-
| || -
15-
| ||_____|
16-
| |______lifetime `'1` represents the closure body
17-
| lifetime `'2` appears in return type
4+
LL | || {
5+
| --
6+
| ||
7+
| |return type of closure is [closure@$DIR/issue-49824.rs:22:9: 24:10 x:&'2 mut i32]
8+
| lifetime `'1` represents this closure's body
9+
LL | / || {
10+
LL | | let _y = &mut x;
11+
LL | | }
12+
| |_________^ requires that `'1` must outlive `'2`
13+
|
14+
= note: closure implements `FnMut`, so references to captured variables can't escape the closure
1815

1916
error: aborting due to previous error
2017

src/test/ui/nll/issue-48238.stderr

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ error: unsatisfied lifetime constraints
22
--> $DIR/issue-48238.rs:21:13
33
|
44
LL | move || use_val(&orig); //~ ERROR
5-
| --------^^^^^^^^^^^^^^
6-
| | |
7-
| | argument requires that `'1` must outlive `'2`
8-
| lifetime `'1` represents the closure body
9-
| lifetime `'2` appears in return type
5+
| ------- ^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
6+
| | |
7+
| | return type of closure is &'2 u8
8+
| lifetime `'1` represents this closure's body
9+
|
10+
= note: closure implements `Fn`, so references to captured variables can't escape the closure
1011

1112
error: aborting due to previous error
1213

0 commit comments

Comments
 (0)