Detect closures assigned to binding in block

Fix #58497.
This commit is contained in:
Esteban Küber 2023-01-05 21:29:36 +00:00
parent 1d284af117
commit ce6b7179af
9 changed files with 75 additions and 36 deletions

View file

@ -440,15 +440,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
closure_kind: &str, closure_kind: &str,
borrowed_path: &str, borrowed_path: &str,
capture_span: Span, capture_span: Span,
scope: &str,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let mut err = struct_span_err!( let mut err = struct_span_err!(
self, self,
closure_span, closure_span,
E0373, E0373,
"{} may outlive the current function, but it borrows {}, which is owned by the current \ "{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \
function", which is owned by the current {scope}",
closure_kind,
borrowed_path,
); );
err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) err.span_label(capture_span, format!("{} is borrowed here", borrowed_path))
.span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path)); .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path));

View file

@ -1423,6 +1423,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// //
// then just use the normal error. The closure isn't escaping // then just use the normal error. The closure isn't escaping
// and `move` will not help here. // and `move` will not help here.
(
Some(name),
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
) => self.report_escaping_closure_capture(
borrow_spans,
borrow_span,
&RegionName {
name: self.synthesize_region_name(),
source: RegionNameSource::Static,
},
ConstraintCategory::CallArgument(None),
var_or_use_span,
&format!("`{}`", name),
"block",
),
( (
Some(name), Some(name),
BorrowExplanation::MustBeValidFor { BorrowExplanation::MustBeValidFor {
@ -1443,6 +1458,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
category, category,
span, span,
&format!("`{}`", name), &format!("`{}`", name),
"function",
), ),
( (
name, name,
@ -1895,6 +1911,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Some(err) Some(err)
} }
#[instrument(level = "debug", skip(self))]
fn report_escaping_closure_capture( fn report_escaping_closure_capture(
&mut self, &mut self,
use_span: UseSpans<'tcx>, use_span: UseSpans<'tcx>,
@ -1903,6 +1920,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
category: ConstraintCategory<'tcx>, category: ConstraintCategory<'tcx>,
constraint_span: Span, constraint_span: Span,
captured_var: &str, captured_var: &str,
scope: &str,
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
let tcx = self.infcx.tcx; let tcx = self.infcx.tcx;
let args_span = use_span.args_or_use(); let args_span = use_span.args_or_use();
@ -1933,8 +1951,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
None => "closure", None => "closure",
}; };
let mut err = let mut err = self.cannot_capture_in_long_lived_closure(
self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span); args_span,
kind,
captured_var,
var_span,
scope,
);
err.span_suggestion_verbose( err.span_suggestion_verbose(
sugg_span, sugg_span,
&format!( &format!(
@ -1956,10 +1979,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) { if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) {
err.note( err.note(
"async blocks are not executed immediately and must either take a \ "async blocks are not executed immediately and must either take a \
reference or ownership of outside variables they use", reference or ownership of outside variables they use",
); );
} else { } else {
let msg = format!("function requires argument type to outlive `{}`", fr_name); let msg = format!("{scope} requires argument type to outlive `{fr_name}`");
err.span_note(constraint_span, &msg); err.span_note(constraint_span, &msg);
} }
} }

View file

@ -444,6 +444,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// First span returned points to the location of the conflicting use /// First span returned points to the location of the conflicting use
/// Second span if `Some` is returned in the case of closures and points /// Second span if `Some` is returned in the case of closures and points
/// to the use of the path /// to the use of the path
#[instrument(level = "debug", skip(self))]
fn later_use_kind( fn later_use_kind(
&self, &self,
borrow: &BorrowData<'tcx>, borrow: &BorrowData<'tcx>,
@ -461,11 +462,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let block = &self.body.basic_blocks[location.block]; let block = &self.body.basic_blocks[location.block];
let kind = if let Some(&Statement { let kind = if let Some(&Statement {
kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)), kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)),
.. ..
}) = block.statements.get(location.statement_index) }) = block.statements.get(location.statement_index)
{ {
LaterUseKind::FakeLetRead if let Some(l) = place.as_local()
&& let local_decl = &self.body.local_decls[l]
&& local_decl.ty.is_closure()
{
LaterUseKind::ClosureCapture
} else {
LaterUseKind::FakeLetRead
}
} else if self.was_captured_by_trait_object(borrow) { } else if self.was_captured_by_trait_object(borrow) {
LaterUseKind::TraitCapture LaterUseKind::TraitCapture
} else if location.statement_index == block.statements.len() { } else if location.statement_index == block.statements.len() {

View file

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

View file

@ -8,10 +8,9 @@ struct Point {
fn main() { fn main() {
let mut c = { let mut c = {
let mut p = Point {x: "1".to_string(), y: "2".to_string() }; let mut p = Point {x: "1".to_string(), y: "2".to_string() };
|| { || { //~ ERROR closure may outlive the current block, but it borrows `p`
let x = &mut p.x; let x = &mut p.x;
println!("{:?}", p); println!("{:?}", p);
//~^ ERROR `p` does not live long enough
} }
}; };
c(); c();

View file

@ -1,18 +1,22 @@
error[E0597]: `p` does not live long enough error[E0373]: closure may outlive the current block, but it borrows `p`, which is owned by the current block
--> $DIR/borrowck-3.rs:13:29 --> $DIR/borrowck-3.rs:11:9
| |
LL | let mut c = {
| ----- borrow later stored here
LL | let mut p = Point {x: "1".to_string(), y: "2".to_string() };
LL | || { LL | || {
| -- value captured here | ^^ may outlive borrowed value `p`
LL | let x = &mut p.x; LL | let x = &mut p.x;
LL | println!("{:?}", p); LL | println!("{:?}", p);
| ^ borrowed value does not live long enough | - `p` is borrowed here
... |
LL | }; note: block requires argument type to outlive `'1`
| - `p` dropped here while still borrowed --> $DIR/borrowck-3.rs:9:9
|
LL | let mut c = {
| ^^^^^
help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword
|
LL | move || {
| ++++
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`. For more information about this error, try `rustc --explain E0373`.

View file

@ -5,7 +5,7 @@
fn main() { fn main() {
let _f = { let _f = {
let x = 0; let x = 0;
|| x //~ ERROR `x` does not live long enough || x //~ ERROR closure may outlive the current block, but it borrows `x`
}; };
_f; _f;
} }

View file

@ -1,16 +1,21 @@
error[E0597]: `x` does not live long enough error[E0373]: closure may outlive the current block, but it borrows `x`, which is owned by the current block
--> $DIR/unboxed-closure-region.rs:8:12 --> $DIR/unboxed-closure-region.rs:8:9
|
LL | || x
| ^^ - `x` is borrowed here
| |
| may outlive borrowed value `x`
|
note: block requires argument type to outlive `'1`
--> $DIR/unboxed-closure-region.rs:6:9
| |
LL | let _f = { LL | let _f = {
| -- borrow later stored here | ^^
LL | let x = 0; help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
LL | || x |
| -- ^ borrowed value does not live long enough LL | move || x
| | | ++++
| value captured here
LL | };
| - `x` dropped here while still borrowed
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`. For more information about this error, try `rustc --explain E0373`.

View file

@ -20,6 +20,7 @@ fn bindings() {
category, category,
span, span,
&format!("`{}`", name), &format!("`{}`", name),
"function",
), ),
( (
ref name, ref name,