From 9cee22c1a401ace6dd8633335b131c47a37d7bac Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 23 May 2020 21:40:55 -0400 Subject: [PATCH] Display information about captured variable in `FnMut` error Fixes #69446 When we encounter a region error involving an `FnMut` closure, we display a specialized error message. However, we currently do not tell the user which upvar was captured. This makes it difficult to determine the cause of the error, especially when the closure is large. This commit records marks constraints involving closure upvars with `ConstraintCategory::ClosureUpvar`. When we decide to 'blame' a `ConstraintCategory::Return`, we additionall store the captured upvar if we found a `ConstraintCategory::ClosureUpvar` in the path. When generating an error message, we point to relevant spans if we have closure upvar information available. We further customize the message if an `async` closure is being returned, to make it clear that the captured variable is being returned indirectly. --- src/libcore/future/mod.rs | 1 + src/librustc_middle/mir/query.rs | 10 +++- .../diagnostics/conflict_errors.rs | 6 +- .../borrow_check/diagnostics/region_errors.rs | 57 +++++++++++++------ src/librustc_mir/borrow_check/mod.rs | 26 +-------- src/librustc_mir/borrow_check/nll.rs | 3 + src/librustc_mir/borrow_check/path_utils.rs | 38 ++++++++++++- .../borrow_check/region_infer/mod.rs | 18 +++++- .../borrow_check/type_check/mod.rs | 27 +++++++-- .../async-await/issue-69446-fnmut-capture.rs | 22 +++++++ .../issue-69446-fnmut-capture.stderr | 19 +++++++ .../borrowck/borrowck-describe-lvalue.stderr | 3 + src/test/ui/issues/issue-40510-1.stderr | 8 ++- src/test/ui/issues/issue-40510-3.stderr | 4 ++ src/test/ui/issues/issue-49824.stderr | 3 + src/test/ui/nll/issue-53040.stderr | 8 ++- ...ons-return-ref-to-upvar-issue-17403.stderr | 8 ++- 17 files changed, 203 insertions(+), 58 deletions(-) create mode 100644 src/test/ui/async-await/issue-69446-fnmut-capture.rs create mode 100644 src/test/ui/async-await/issue-69446-fnmut-capture.stderr diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs index 6f6009b47e672..9dbc23f5c04c5 100644 --- a/src/libcore/future/mod.rs +++ b/src/libcore/future/mod.rs @@ -56,6 +56,7 @@ pub const fn from_generator(gen: T) -> impl Future where T: Generator, { + #[rustc_diagnostic_item = "gen_future"] struct GenFuture>(T); // We rely on the fact that async/await futures are immovable in order to create diff --git a/src/librustc_middle/mir/query.rs b/src/librustc_middle/mir/query.rs index 63b8d8c8da782..aed93e86863b9 100644 --- a/src/librustc_middle/mir/query.rs +++ b/src/librustc_middle/mir/query.rs @@ -171,7 +171,7 @@ pub struct ClosureOutlivesRequirement<'tcx> { #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] #[derive(RustcEncodable, RustcDecodable, HashStable)] pub enum ConstraintCategory { - Return, + Return(ReturnConstraint), Yield, UseAsConst, UseAsStatic, @@ -187,6 +187,7 @@ pub enum ConstraintCategory { SizedBound, Assignment, OpaqueType, + ClosureUpvar(hir::HirId), /// A "boring" constraint (caused by the given location) is one that /// the user probably doesn't want to see described in diagnostics, @@ -204,6 +205,13 @@ pub enum ConstraintCategory { Internal, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(RustcEncodable, RustcDecodable, HashStable)] +pub enum ReturnConstraint { + Normal, + ClosureUpvar(hir::HirId), +} + /// The subject of a `ClosureOutlivesRequirement` -- that is, the thing /// that must outlive some region. #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index 5f1c0911da2bf..b0b5a819983a8 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -766,7 +766,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { category: category @ - (ConstraintCategory::Return + (ConstraintCategory::Return(_) | ConstraintCategory::CallArgument | ConstraintCategory::OpaqueType), from_closure: false, @@ -1096,7 +1096,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { opt_place_desc: Option<&String>, ) -> Option> { let return_kind = match category { - ConstraintCategory::Return => "return", + ConstraintCategory::Return(_) => "return", ConstraintCategory::Yield => "yield", _ => return None, }; @@ -1210,7 +1210,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); let msg = match category { - ConstraintCategory::Return | ConstraintCategory::OpaqueType => { + ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => { format!("{} is returned here", kind) } ConstraintCategory::CallArgument => { diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs index e19fab89eabfe..681f999e048c1 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -5,9 +5,9 @@ use rustc_infer::infer::{ error_reporting::nice_region_error::NiceRegionError, error_reporting::unexpected_hidden_region_diagnostic, NLLRegionVariableOrigin, }; -use rustc_middle::mir::ConstraintCategory; +use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use crate::util::borrowck_errors; @@ -26,7 +26,7 @@ impl ConstraintDescription for ConstraintCategory { // Must end with a space. Allows for empty names to be provided. match self { ConstraintCategory::Assignment => "assignment ", - ConstraintCategory::Return => "returning this value ", + ConstraintCategory::Return(_) => "returning this value ", ConstraintCategory::Yield => "yielding this value ", ConstraintCategory::UseAsConst => "using this value as a constant ", ConstraintCategory::UseAsStatic => "using this value as a static ", @@ -37,6 +37,7 @@ impl ConstraintDescription for ConstraintCategory { ConstraintCategory::SizedBound => "proving this value is `Sized` ", ConstraintCategory::CopyBound => "copying this value ", ConstraintCategory::OpaqueType => "opaque type ", + ConstraintCategory::ClosureUpvar(_) => "closure capture ", ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => "", @@ -306,8 +307,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; let diag = match (category, fr_is_local, outlived_fr_is_local) { - (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(fr) => { - self.report_fnmut_error(&errci) + (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => { + self.report_fnmut_error(&errci, kind) } (ConstraintCategory::Assignment, true, false) | (ConstraintCategory::CallArgument, true, false) => { @@ -347,7 +348,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// executing... /// = note: ...therefore, returned references to captured variables will escape the closure /// ``` - fn report_fnmut_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> { + fn report_fnmut_error( + &self, + errci: &ErrorConstraintInfo, + kind: ReturnConstraint, + ) -> DiagnosticBuilder<'tcx> { let ErrorConstraintInfo { outlived_fr, span, .. } = errci; let mut diag = self @@ -356,19 +361,39 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .sess .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body"); - // We should check if the return type of this closure is in fact a closure - in that - // case, we can special case the error further. - let return_type_is_closure = - self.regioncx.universal_regions().unnormalized_output_ty.is_closure(); - let message = if return_type_is_closure { - "returns a closure that contains a reference to a captured variable, which then \ - escapes the closure body" - } else { - "returns a reference to a captured variable which escapes the closure body" + let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; + if let ty::Opaque(def_id, _) = output_ty.kind { + output_ty = self.infcx.tcx.type_of(def_id) + }; + + debug!("report_fnmut_error: output_ty={:?}", output_ty); + + let message = match output_ty.kind { + ty::Closure(_, _) => { + "returns a closure that contains a reference to a captured variable, which then \ + escapes the closure body" + } + ty::Adt(def, _) if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did) => { + "returns an `async` block that contains a reference to a captured variable, which then \ + escapes the closure body" + } + _ => "returns a reference to a captured variable which escapes the closure body", }; diag.span_label(*span, message); + if let ReturnConstraint::ClosureUpvar(upvar) = kind { + let def_id = match self.regioncx.universal_regions().defining_ty { + DefiningTy::Closure(def_id, _) => def_id, + ty @ _ => bug!("unexpected DefiningTy {:?}", ty), + }; + + let upvar_def_span = self.infcx.tcx.hir().span(upvar); + let upvar_span = self.infcx.tcx.upvars_mentioned(def_id).unwrap()[&upvar].span; + diag.span_label(upvar_def_span, "variable defined here"); + diag.span_label(upvar_span, "variable captured here"); + } + match self.give_region_a_name(*outlived_fr).unwrap().source { RegionNameSource::NamedEarlyBoundRegion(fr_span) | RegionNameSource::NamedFreeRegion(fr_span) @@ -506,7 +531,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { outlived_fr_name.highlight_region_name(&mut diag); match (category, outlived_fr_is_local, fr_is_local) { - (ConstraintCategory::Return, true, _) => { + (ConstraintCategory::Return(_), true, _) => { diag.span_label( *span, format!( diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index a0c1d96bb4743..78d88ac81aaf8 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -218,6 +218,7 @@ fn do_mir_borrowck<'a, 'tcx>( &mut flow_inits, &mdpe.move_data, &borrow_set, + &upvars, ); // Dump MIR results into a file, if that is enabled. This let us @@ -2301,30 +2302,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// be `self` in the current MIR, because that is the only time we directly access the fields /// of a closure type. pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option { - let mut place_projection = place_ref.projection; - let mut by_ref = false; - - if let [proj_base @ .., ProjectionElem::Deref] = place_projection { - place_projection = proj_base; - by_ref = true; - } - - match place_projection { - [base @ .., ProjectionElem::Field(field, _ty)] => { - let tcx = self.infcx.tcx; - let base_ty = Place::ty_from(place_ref.local, base, self.body(), tcx).ty; - - if (base_ty.is_closure() || base_ty.is_generator()) - && (!by_ref || self.upvars[field.index()].by_ref) - { - Some(*field) - } else { - None - } - } - - _ => None, - } + path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body()) } } diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index b820b79c47fe8..5bae31cafb953 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/src/librustc_mir/borrow_check/nll.rs @@ -39,6 +39,7 @@ use crate::borrow_check::{ renumber, type_check::{self, MirTypeckRegionConstraints, MirTypeckResults}, universal_regions::UniversalRegions, + Upvar, }; crate type PoloniusOutput = Output; @@ -166,6 +167,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, + upvars: &[Upvar], ) -> NllOutput<'tcx> { let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default()); @@ -188,6 +190,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( flow_inits, move_data, elements, + upvars, ); if let Some(all_facts) = &mut all_facts { diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index f5238e7b7bedc..934729553a73b 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -1,10 +1,11 @@ use crate::borrow_check::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; use crate::borrow_check::places_conflict; use crate::borrow_check::AccessDepth; +use crate::borrow_check::Upvar; use crate::dataflow::indexes::BorrowIndex; use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::mir::BorrowKind; -use rustc_middle::mir::{BasicBlock, Body, Location, Place}; +use rustc_middle::mir::{BasicBlock, Body, Field, Location, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::TyCtxt; /// Returns `true` if the borrow represented by `kind` is @@ -135,3 +136,38 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool { // Any errors will be caught on the initial borrow !place.is_indirect() } + +/// If `place` is a field projection, and the field is being projected from a closure type, +/// then returns the index of the field being projected. Note that this closure will always +/// be `self` in the current MIR, because that is the only time we directly access the fields +/// of a closure type. +pub(crate) fn is_upvar_field_projection( + tcx: TyCtxt<'tcx>, + upvars: &[Upvar], + place_ref: PlaceRef<'tcx>, + body: &Body<'tcx>, +) -> Option { + let mut place_projection = place_ref.projection; + let mut by_ref = false; + + if let [proj_base @ .., ProjectionElem::Deref] = place_projection { + place_projection = proj_base; + by_ref = true; + } + + match place_projection { + [base @ .., ProjectionElem::Field(field, _ty)] => { + let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty; + + if (base_ty.is_closure() || base_ty.is_generator()) + && (!by_ref || upvars[field.index()].by_ref) + { + Some(*field) + } else { + None + } + } + + _ => None, + } +} diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index fe11384380086..3e459bd52f757 100644 --- a/src/librustc_mir/borrow_check/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/region_infer/mod.rs @@ -12,7 +12,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound} use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::mir::{ Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - ConstraintCategory, Local, Location, + ConstraintCategory, Local, Location, ReturnConstraint, }; use rustc_middle::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -2017,7 +2017,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { | ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false, ConstraintCategory::TypeAnnotation - | ConstraintCategory::Return + | ConstraintCategory::Return(_) | ConstraintCategory::Yield => true, _ => constraint_sup_scc != target_scc, } @@ -2042,7 +2042,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let Some(i) = best_choice { if let Some(next) = categorized_path.get(i + 1) { - if categorized_path[i].0 == ConstraintCategory::Return + if matches!(categorized_path[i].0, ConstraintCategory::Return(_)) && next.0 == ConstraintCategory::OpaqueType { // The return expression is being influenced by the return type being @@ -2050,6 +2050,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { return *next; } } + + if categorized_path[i].0 == ConstraintCategory::Return(ReturnConstraint::Normal) { + let field = categorized_path.iter().find_map(|p| { + if let ConstraintCategory::ClosureUpvar(f) = p.0 { Some(f) } else { None } + }); + + if let Some(field) = field { + categorized_path[i].0 = + ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); + } + } + return categorized_path[i]; } diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index bdbce1de745ad..d6b06bc1dfa64 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -55,6 +55,7 @@ use crate::borrow_check::{ location::LocationTable, member_constraints::MemberConstraintSet, nll::ToRegionVid, + path_utils, region_infer::values::{ LivenessValues, PlaceholderIndex, PlaceholderIndices, RegionValueElements, }, @@ -62,6 +63,7 @@ use crate::borrow_check::{ renumber, type_check::free_region_relations::{CreateResult, UniversalRegionRelations}, universal_regions::{DefiningTy, UniversalRegions}, + Upvar, }; macro_rules! span_mirbug { @@ -132,6 +134,7 @@ pub(crate) fn type_check<'mir, 'tcx>( flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc, + upvars: &[Upvar], ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { @@ -162,6 +165,7 @@ pub(crate) fn type_check<'mir, 'tcx>( borrow_set, all_facts, constraints: &mut constraints, + upvars, }; let opaque_type_values = type_check_internal( @@ -577,7 +581,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { for constraint in constraints.outlives().iter() { let mut constraint = *constraint; constraint.locations = locations; - if let ConstraintCategory::Return + if let ConstraintCategory::Return(_) | ConstraintCategory::UseAsConst | ConstraintCategory::UseAsStatic = constraint.category { @@ -827,6 +831,7 @@ struct BorrowCheckContext<'a, 'tcx> { all_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, + upvars: &'a [Upvar], } crate struct MirTypeckResults<'tcx> { @@ -1420,7 +1425,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::UseAsConst } } else { - ConstraintCategory::Return + ConstraintCategory::Return(ReturnConstraint::Normal) } } Some(l) if !body.local_decls[l].is_user_variable() => { @@ -1703,7 +1708,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::UseAsConst } } else { - ConstraintCategory::Return + ConstraintCategory::Return(ReturnConstraint::Normal) } } Some(l) if !body.local_decls[l].is_user_variable() => { @@ -2487,6 +2492,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); let mut cursor = borrowed_place.projection.as_ref(); + let tcx = self.infcx.tcx; + let field = path_utils::is_upvar_field_projection( + tcx, + &self.borrowck_context.upvars, + borrowed_place.as_ref(), + body, + ); + let category = if let Some(field) = field { + ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id) + } else { + ConstraintCategory::Boring + }; + while let [proj_base @ .., elem] = cursor { cursor = proj_base; @@ -2494,7 +2512,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { match elem { ProjectionElem::Deref => { - let tcx = self.infcx.tcx; let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty; debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); @@ -2504,7 +2521,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { sup: ref_region.to_region_vid(), sub: borrow_region.to_region_vid(), locations: location.to_locations(), - category: ConstraintCategory::Boring, + category, }); match mutbl { diff --git a/src/test/ui/async-await/issue-69446-fnmut-capture.rs b/src/test/ui/async-await/issue-69446-fnmut-capture.rs new file mode 100644 index 0000000000000..842115538c9d8 --- /dev/null +++ b/src/test/ui/async-await/issue-69446-fnmut-capture.rs @@ -0,0 +1,22 @@ +// Regression test for issue #69446 - we should display +// which variable is captured +// edition:2018 + +use core::future::Future; + +struct Foo; +impl Foo { + fn foo(&mut self) {} +} + +async fn bar(_: impl FnMut() -> T) +where + T: Future, +{} + +fn main() { + let mut x = Foo; + bar(move || async { //~ ERROR captured + x.foo(); + }); +} diff --git a/src/test/ui/async-await/issue-69446-fnmut-capture.stderr b/src/test/ui/async-await/issue-69446-fnmut-capture.stderr new file mode 100644 index 0000000000000..3d2b0402bc52c --- /dev/null +++ b/src/test/ui/async-await/issue-69446-fnmut-capture.stderr @@ -0,0 +1,19 @@ +error: captured variable cannot escape `FnMut` closure body + --> $DIR/issue-69446-fnmut-capture.rs:19:17 + | +LL | let mut x = Foo; + | ----- variable defined here +LL | bar(move || async { + | _______________-_^ + | | | + | | inferred to be a `FnMut` closure +LL | | x.foo(); + | | - variable captured here +LL | | }); + | |_____^ returns an `async` block that contains a reference to a captured variable, which then escapes the closure body + | + = note: `FnMut` closures only have access to their captured variables while they are executing... + = note: ...therefore, they cannot allow references to captured variables to escape + +error: aborting due to previous error + diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr index 075e0e2e4515e..4144d70cc1601 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr @@ -21,10 +21,13 @@ LL | *y = 1; error: captured variable cannot escape `FnMut` closure body --> $DIR/borrowck-describe-lvalue.rs:264:16 | +LL | let mut x = 0; + | ----- variable defined here LL | || { | - inferred to be a `FnMut` closure LL | / || { LL | | let y = &mut x; + | | - variable captured here LL | | &mut x; LL | | *y = 1; LL | | drop(y); diff --git a/src/test/ui/issues/issue-40510-1.stderr b/src/test/ui/issues/issue-40510-1.stderr index f4fda0abc2049..54df40b6e3d05 100644 --- a/src/test/ui/issues/issue-40510-1.stderr +++ b/src/test/ui/issues/issue-40510-1.stderr @@ -1,10 +1,16 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-40510-1.rs:7:9 | +LL | let mut x: Box<()> = Box::new(()); + | ----- variable defined here +LL | LL | || { | - inferred to be a `FnMut` closure LL | &mut x - | ^^^^^^ returns a reference to a captured variable which escapes the closure body + | ^^^^^- + | | | + | | variable captured here + | returns a reference to a captured variable which escapes the closure body | = note: `FnMut` closures only have access to their captured variables while they are executing... = note: ...therefore, they cannot allow references to captured variables to escape diff --git a/src/test/ui/issues/issue-40510-3.stderr b/src/test/ui/issues/issue-40510-3.stderr index 4bc7d0f5deac5..cb885ec7d952a 100644 --- a/src/test/ui/issues/issue-40510-3.stderr +++ b/src/test/ui/issues/issue-40510-3.stderr @@ -1,10 +1,14 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-40510-3.rs:7:9 | +LL | let mut x: Vec<()> = Vec::new(); + | ----- variable defined here +LL | LL | || { | - inferred to be a `FnMut` closure LL | / || { LL | | x.push(()) + | | - variable captured here LL | | } | |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body | diff --git a/src/test/ui/issues/issue-49824.stderr b/src/test/ui/issues/issue-49824.stderr index 6b486aafcdf40..2fec482543d7b 100644 --- a/src/test/ui/issues/issue-49824.stderr +++ b/src/test/ui/issues/issue-49824.stderr @@ -1,11 +1,14 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-49824.rs:4:9 | +LL | let mut x = 0; + | ----- variable defined here LL | || { | - inferred to be a `FnMut` closure LL | / || { LL | | LL | | let _y = &mut x; + | | - variable captured here LL | | } | |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body | diff --git a/src/test/ui/nll/issue-53040.stderr b/src/test/ui/nll/issue-53040.stderr index 7cba32c67432c..87ffe9b1abf45 100644 --- a/src/test/ui/nll/issue-53040.stderr +++ b/src/test/ui/nll/issue-53040.stderr @@ -1,9 +1,13 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-53040.rs:3:8 | +LL | let mut v: Vec<()> = Vec::new(); + | ----- variable defined here LL | || &mut v; - | - ^^^^^^ returns a reference to a captured variable which escapes the closure body - | | + | - ^^^^^- + | | | | + | | | variable captured here + | | returns a reference to a captured variable which escapes the closure body | inferred to be a `FnMut` closure | = note: `FnMut` closures only have access to their captured variables while they are executing... diff --git a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr index 4c275b19492c6..b087e03b464b3 100644 --- a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr +++ b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr @@ -1,9 +1,13 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:7:24 | +LL | let mut x = 0; + | ----- variable defined here LL | let mut f = || &mut x; - | - ^^^^^^ returns a reference to a captured variable which escapes the closure body - | | + | - ^^^^^- + | | | | + | | | variable captured here + | | returns a reference to a captured variable which escapes the closure body | inferred to be a `FnMut` closure | = note: `FnMut` closures only have access to their captured variables while they are executing...