Skip to content

Commit 1e6710d

Browse files
authored
Rollup merge of #106509 - estebank:closure-in-block, r=davidtwco
Detect closures assigned to binding in block Fix #58497.
2 parents 0c8d11b + ce6b717 commit 1e6710d

File tree

9 files changed

+74
-35
lines changed

9 files changed

+74
-35
lines changed

compiler/rustc_borrowck/src/borrowck_errors.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -440,15 +440,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
440440
closure_kind: &str,
441441
borrowed_path: &str,
442442
capture_span: Span,
443+
scope: &str,
443444
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
444445
let mut err = struct_span_err!(
445446
self,
446447
closure_span,
447448
E0373,
448-
"{} may outlive the current function, but it borrows {}, which is owned by the current \
449-
function",
450-
closure_kind,
451-
borrowed_path,
449+
"{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \
450+
which is owned by the current {scope}",
452451
);
453452
err.span_label(capture_span, format!("{} is borrowed here", borrowed_path))
454453
.span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path));

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
14231423
//
14241424
// then just use the normal error. The closure isn't escaping
14251425
// and `move` will not help here.
1426+
(
1427+
Some(name),
1428+
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
1429+
) => self.report_escaping_closure_capture(
1430+
borrow_spans,
1431+
borrow_span,
1432+
&RegionName {
1433+
name: self.synthesize_region_name(),
1434+
source: RegionNameSource::Static,
1435+
},
1436+
ConstraintCategory::CallArgument(None),
1437+
var_or_use_span,
1438+
&format!("`{}`", name),
1439+
"block",
1440+
),
14261441
(
14271442
Some(name),
14281443
BorrowExplanation::MustBeValidFor {
@@ -1443,6 +1458,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
14431458
category,
14441459
span,
14451460
&format!("`{}`", name),
1461+
"function",
14461462
),
14471463
(
14481464
name,
@@ -1895,6 +1911,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
18951911
Some(err)
18961912
}
18971913

1914+
#[instrument(level = "debug", skip(self))]
18981915
fn report_escaping_closure_capture(
18991916
&mut self,
19001917
use_span: UseSpans<'tcx>,
@@ -1903,6 +1920,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
19031920
category: ConstraintCategory<'tcx>,
19041921
constraint_span: Span,
19051922
captured_var: &str,
1923+
scope: &str,
19061924
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
19071925
let tcx = self.infcx.tcx;
19081926
let args_span = use_span.args_or_use();
@@ -1933,8 +1951,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
19331951
None => "closure",
19341952
};
19351953

1936-
let mut err =
1937-
self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span);
1954+
let mut err = self.cannot_capture_in_long_lived_closure(
1955+
args_span,
1956+
kind,
1957+
captured_var,
1958+
var_span,
1959+
scope,
1960+
);
19381961
err.span_suggestion_verbose(
19391962
sugg_span,
19401963
&format!(
@@ -1956,10 +1979,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
19561979
if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
19571980
err.note(
19581981
"async blocks are not executed immediately and must either take a \
1959-
reference or ownership of outside variables they use",
1982+
reference or ownership of outside variables they use",
19601983
);
19611984
} else {
1962-
let msg = format!("function requires argument type to outlive `{}`", fr_name);
1985+
let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
19631986
err.span_note(constraint_span, &msg);
19641987
}
19651988
}

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
444444
/// First span returned points to the location of the conflicting use
445445
/// Second span if `Some` is returned in the case of closures and points
446446
/// to the use of the path
447+
#[instrument(level = "debug", skip(self))]
447448
fn later_use_kind(
448449
&self,
449450
borrow: &BorrowData<'tcx>,
@@ -461,11 +462,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
461462
let block = &self.body.basic_blocks[location.block];
462463

463464
let kind = if let Some(&Statement {
464-
kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),
465+
kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)),
465466
..
466467
}) = block.statements.get(location.statement_index)
467468
{
468-
LaterUseKind::FakeLetRead
469+
if let Some(l) = place.as_local()
470+
&& let local_decl = &self.body.local_decls[l]
471+
&& local_decl.ty.is_closure()
472+
{
473+
LaterUseKind::ClosureCapture
474+
} else {
475+
LaterUseKind::FakeLetRead
476+
}
469477
} else if self.was_captured_by_trait_object(borrow) {
470478
LaterUseKind::TraitCapture
471479
} else if location.statement_index == block.statements.len() {

compiler/rustc_borrowck/src/diagnostics/region_name.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
200200
/// increment the counter.
201201
///
202202
/// This is _not_ idempotent. Call `give_region_a_name` when possible.
203-
fn synthesize_region_name(&self) -> Symbol {
203+
pub(crate) fn synthesize_region_name(&self) -> Symbol {
204204
let c = self.next_region_name.replace_with(|counter| *counter + 1);
205205
Symbol::intern(&format!("'{:?}", c))
206206
}

src/test/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ struct Point {
88
fn main() {
99
let mut c = {
1010
let mut p = Point {x: "1".to_string(), y: "2".to_string() };
11-
|| {
11+
|| { //~ ERROR closure may outlive the current block, but it borrows `p`
1212
let x = &mut p.x;
1313
println!("{:?}", p);
14-
//~^ ERROR `p` does not live long enough
1514
}
1615
};
1716
c();
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
error[E0597]: `p` does not live long enough
2-
--> $DIR/borrowck-3.rs:13:29
1+
error[E0373]: closure may outlive the current block, but it borrows `p`, which is owned by the current block
2+
--> $DIR/borrowck-3.rs:11:9
33
|
4-
LL | let mut c = {
5-
| ----- borrow later stored here
6-
LL | let mut p = Point {x: "1".to_string(), y: "2".to_string() };
74
LL | || {
8-
| -- value captured here
5+
| ^^ may outlive borrowed value `p`
96
LL | let x = &mut p.x;
107
LL | println!("{:?}", p);
11-
| ^ borrowed value does not live long enough
12-
...
13-
LL | };
14-
| - `p` dropped here while still borrowed
8+
| - `p` is borrowed here
9+
|
10+
note: block requires argument type to outlive `'1`
11+
--> $DIR/borrowck-3.rs:9:9
12+
|
13+
LL | let mut c = {
14+
| ^^^^^
15+
help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword
16+
|
17+
LL | move || {
18+
| ++++
1519

1620
error: aborting due to previous error
1721

18-
For more information about this error, try `rustc --explain E0597`.
22+
For more information about this error, try `rustc --explain E0373`.

src/test/ui/unboxed-closures/unboxed-closure-region.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
fn main() {
66
let _f = {
77
let x = 0;
8-
|| x //~ ERROR `x` does not live long enough
8+
|| x //~ ERROR closure may outlive the current block, but it borrows `x`
99
};
1010
_f;
1111
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
error[E0597]: `x` does not live long enough
2-
--> $DIR/unboxed-closure-region.rs:8:12
1+
error[E0373]: closure may outlive the current block, but it borrows `x`, which is owned by the current block
2+
--> $DIR/unboxed-closure-region.rs:8:9
33
|
4-
LL | let _f = {
5-
| -- borrow later stored here
6-
LL | let x = 0;
74
LL | || x
8-
| -- ^ borrowed value does not live long enough
5+
| ^^ - `x` is borrowed here
96
| |
10-
| value captured here
11-
LL | };
12-
| - `x` dropped here while still borrowed
7+
| may outlive borrowed value `x`
8+
|
9+
note: block requires argument type to outlive `'1`
10+
--> $DIR/unboxed-closure-region.rs:6:9
11+
|
12+
LL | let _f = {
13+
| ^^
14+
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
15+
|
16+
LL | move || x
17+
| ++++
1318

1419
error: aborting due to previous error
1520

16-
For more information about this error, try `rustc --explain E0597`.
21+
For more information about this error, try `rustc --explain E0373`.

src/tools/rustfmt/tests/target/issue_4110.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fn bindings() {
2020
category,
2121
span,
2222
&format!("`{}`", name),
23+
"function",
2324
),
2425
(
2526
ref name,

0 commit comments

Comments
 (0)