@@ -5,6 +5,7 @@ use core::ops::ControlFlow;
5
5
6
6
use hir:: { ExprKind , Param } ;
7
7
use rustc_abi:: FieldIdx ;
8
+ use rustc_data_structures:: fx:: FxHashMap ;
8
9
use rustc_errors:: { Applicability , Diag } ;
9
10
use rustc_hir:: intravisit:: Visitor ;
10
11
use rustc_hir:: { self as hir, BindingMode , ByRef , Node } ;
@@ -122,8 +123,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
122
123
}
123
124
}
124
125
}
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
127
128
&& proj_base. is_empty ( )
128
129
&& !self . upvars . is_empty ( )
129
130
{
@@ -137,10 +138,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
137
138
", as `Fn` closures cannot mutate their captured variables" . to_string ( )
138
139
}
139
140
} 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 } ) ;
144
143
let pointer_type = source. describe_for_immutable_place ( self . infcx . tcx ) ;
145
144
opt_source = Some ( source) ;
146
145
if let Some ( desc) = self . describe_place ( access_place. as_ref ( ) ) {
@@ -506,6 +505,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
506
505
PlaceRef { local, projection : [ ProjectionElem :: Deref ] }
507
506
if local == ty:: CAPTURE_STRUCT_LOCAL && !self . upvars . is_empty ( ) =>
508
507
{
508
+ self . point_at_binding_outside_closure ( & mut err, local, access_place) ;
509
509
self . expected_fn_found_fn_mut_call ( & mut err, span, act) ;
510
510
}
511
511
@@ -929,6 +929,76 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
929
929
}
930
930
}
931
931
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
+ }
932
1002
/// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
933
1003
fn expected_fn_found_fn_mut_call ( & self , err : & mut Diag < ' _ > , sp : Span , act : & str ) {
934
1004
err. span_label ( sp, format ! ( "cannot {act}" ) ) ;
@@ -941,6 +1011,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
941
1011
let def_id = hir. enclosing_body_owner ( fn_call_id) ;
942
1012
let mut look_at_return = true ;
943
1013
1014
+ err. span_label ( closure_span, "in this closure" ) ;
944
1015
// If the HIR node is a function or method call gets the def ID
945
1016
// of the called function or method and the span and args of the call expr
946
1017
let get_call_details = || {
@@ -1009,7 +1080,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
1009
1080
if let Some ( span) = arg {
1010
1081
err. span_label ( span, "change this to accept `FnMut` instead of `Fn`" ) ;
1011
1082
err. span_label ( call_span, "expects `Fn` instead of `FnMut`" ) ;
1012
- err. span_label ( closure_span, "in this closure" ) ;
1013
1083
look_at_return = false ;
1014
1084
}
1015
1085
}
@@ -1034,7 +1104,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
1034
1104
sig. decl . output . span ( ) ,
1035
1105
"change this to return `FnMut` instead of `Fn`" ,
1036
1106
) ;
1037
- err. span_label ( closure_span, "in this closure" ) ;
1038
1107
}
1039
1108
_ => { }
1040
1109
}
0 commit comments