@@ -5,6 +5,7 @@ use core::ops::ControlFlow;
55
66use hir:: { ExprKind , Param } ;
77use rustc_abi:: FieldIdx ;
8+ use rustc_data_structures:: fx:: FxHashMap ;
89use rustc_errors:: { Applicability , Diag } ;
910use rustc_hir:: intravisit:: Visitor ;
1011use rustc_hir:: { self as hir, BindingMode , ByRef , Node } ;
@@ -122,8 +123,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
122123 }
123124 }
124125 }
125- PlaceRef { local : _ , projection : [ proj_base @ .., ProjectionElem :: Deref ] } => {
126- if the_place_err . local == ty:: CAPTURE_STRUCT_LOCAL
126+ PlaceRef { local, projection : [ proj_base @ .., ProjectionElem :: Deref ] } => {
127+ if local == ty:: CAPTURE_STRUCT_LOCAL
127128 && proj_base. is_empty ( )
128129 && !self . upvars . is_empty ( )
129130 {
@@ -137,10 +138,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
137138 ", as `Fn` closures cannot mutate their captured variables" . to_string ( )
138139 }
139140 } else {
140- let source = self . borrowed_content_source ( PlaceRef {
141- local : the_place_err. local ,
142- projection : proj_base,
143- } ) ;
141+ let source =
142+ self . borrowed_content_source ( PlaceRef { local, projection : proj_base } ) ;
144143 let pointer_type = source. describe_for_immutable_place ( self . infcx . tcx ) ;
145144 opt_source = Some ( source) ;
146145 if let Some ( desc) = self . describe_place ( access_place. as_ref ( ) ) {
@@ -506,6 +505,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
506505 PlaceRef { local, projection : [ ProjectionElem :: Deref ] }
507506 if local == ty:: CAPTURE_STRUCT_LOCAL && !self . upvars . is_empty ( ) =>
508507 {
508+ self . point_at_binding_outside_closure ( & mut err, local, access_place) ;
509509 self . expected_fn_found_fn_mut_call ( & mut err, span, act) ;
510510 }
511511
@@ -929,6 +929,76 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
929929 }
930930 }
931931
932+ /// When modifying a binding from inside of an `Fn` closure, point at the binding definition
933+ /// and suggest using an `std::sync` type that would allow the code to compile.
934+ fn point_at_binding_outside_closure (
935+ & self ,
936+ err : & mut Diag < ' _ > ,
937+ local : Local ,
938+ access_place : Place < ' tcx > ,
939+ ) {
940+ let place = access_place. as_ref ( ) ;
941+ for ( index, elem) in place. projection . into_iter ( ) . enumerate ( ) {
942+ if let ProjectionElem :: Deref = elem {
943+ if index == 0 {
944+ if self . body . local_decls [ local] . is_ref_for_guard ( ) {
945+ continue ;
946+ }
947+ if let LocalInfo :: StaticRef { .. } = * self . body . local_decls [ local] . local_info ( )
948+ {
949+ continue ;
950+ }
951+ }
952+ if let Some ( field) = self . is_upvar_field_projection ( PlaceRef {
953+ local,
954+ projection : place. projection . split_at ( index + 1 ) . 0 ,
955+ } ) {
956+ let var_index = field. index ( ) ;
957+ let upvar = self . upvars [ var_index] ;
958+ if let Some ( hir_id) = upvar. info . capture_kind_expr_id {
959+ let node = self . infcx . tcx . hir_node ( hir_id) ;
960+ if let hir:: Node :: Expr ( expr) = node
961+ && let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
962+ && let hir:: def:: Res :: Local ( hir_id) = path. res
963+ && let hir:: Node :: Pat ( pat) = self . infcx . tcx . hir_node ( hir_id)
964+ {
965+ let hir = self . infcx . tcx . hir ( ) ;
966+ let def_id = hir. enclosing_body_owner ( self . mir_hir_id ( ) ) ;
967+ let typeck_results = self . infcx . tcx . typeck ( def_id) ;
968+ let ty = typeck_results. node_type_opt ( expr. hir_id ) ;
969+ if let Some ( ty) = ty {
970+ let mutex = format ! ( "std::sync::atomic::Mutex<{ty}>" ) ;
971+ let mutex = mutex. as_str ( ) ;
972+ let suggestions: FxHashMap < _ , _ > = [
973+ ( self . infcx . tcx . types . isize , "std::sync::atomic::AtomicIsize" ) ,
974+ ( self . infcx . tcx . types . usize , "std::sync::atomic::AtomicUsize" ) ,
975+ ( self . infcx . tcx . types . i64 , "std::sync::atomic::AtomicI64" ) ,
976+ ( self . infcx . tcx . types . u64 , "std::sync::atomic::AtomicU64" ) ,
977+ ( self . infcx . tcx . types . i32 , "std::sync::atomic::AtomicI32" ) ,
978+ ( self . infcx . tcx . types . u32 , "std::sync::atomic::AtomicU32" ) ,
979+ ( self . infcx . tcx . types . i16 , "std::sync::atomic::AtomicI16" ) ,
980+ ( self . infcx . tcx . types . u16 , "std::sync::atomic::AtomicU16" ) ,
981+ ( self . infcx . tcx . types . i8 , "std::sync::atomic::AtomicI8" ) ,
982+ ( self . infcx . tcx . types . u8 , "std::sync::atomic::AtomicU8" ) ,
983+ ( self . infcx . tcx . types . bool , "std::sync::atomic::AtomicBool" ) ,
984+ ]
985+ . into_iter ( )
986+ . collect ( ) ;
987+ let ty = suggestions. get ( & ty) . unwrap_or ( & mutex) ;
988+ err. help ( format ! ( "consider using `{ty}` instead, which allows for multiple threads to access and modify the value" ) ) ;
989+ }
990+ let name = upvar. to_string ( self . infcx . tcx ) ;
991+ err. span_label (
992+ pat. span ,
993+ format ! ( "`{name}` declared here, outside the closure" ) ,
994+ ) ;
995+ break ;
996+ }
997+ }
998+ }
999+ }
1000+ }
1001+ }
9321002 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
9331003 fn expected_fn_found_fn_mut_call ( & self , err : & mut Diag < ' _ > , sp : Span , act : & str ) {
9341004 err. span_label ( sp, format ! ( "cannot {act}" ) ) ;
@@ -941,6 +1011,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
9411011 let def_id = hir. enclosing_body_owner ( fn_call_id) ;
9421012 let mut look_at_return = true ;
9431013
1014+ err. span_label ( closure_span, "in this closure" ) ;
9441015 // If the HIR node is a function or method call gets the def ID
9451016 // of the called function or method and the span and args of the call expr
9461017 let get_call_details = || {
@@ -1009,7 +1080,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
10091080 if let Some ( span) = arg {
10101081 err. span_label ( span, "change this to accept `FnMut` instead of `Fn`" ) ;
10111082 err. span_label ( call_span, "expects `Fn` instead of `FnMut`" ) ;
1012- err. span_label ( closure_span, "in this closure" ) ;
10131083 look_at_return = false ;
10141084 }
10151085 }
@@ -1034,7 +1104,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
10341104 sig. decl . output . span ( ) ,
10351105 "change this to return `FnMut` instead of `Fn`" ,
10361106 ) ;
1037- err. span_label ( closure_span, "in this closure" ) ;
10381107 }
10391108 _ => { }
10401109 }
0 commit comments