@@ -15,11 +15,11 @@ use rustc::hir;
15
15
use rustc:: hir:: def_id:: DefId ;
16
16
use rustc:: middle:: region:: ScopeTree ;
17
17
use rustc:: mir:: {
18
- self , AggregateKind , BindingForm , BorrowKind , ClearCrossCrate , Field , Local ,
18
+ self , AggregateKind , BindingForm , BorrowKind , ClearCrossCrate , Constant , Field , Local ,
19
19
LocalDecl , LocalKind , Location , Operand , Place , PlaceProjection , ProjectionElem ,
20
20
Rvalue , Statement , StatementKind , TerminatorKind , VarBindingForm ,
21
21
} ;
22
- use rustc:: ty;
22
+ use rustc:: ty:: { self , DefIdTree } ;
23
23
use rustc:: util:: ppaux:: with_highlight_region_for_bound_region;
24
24
use rustc_data_structures:: fx:: FxHashSet ;
25
25
use rustc_data_structures:: sync:: Lrc ;
@@ -131,6 +131,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
131
131
Origin :: Mir ,
132
132
) ;
133
133
134
+ self . add_closure_invoked_twice_with_moved_variable_suggestion (
135
+ context. loc ,
136
+ used_place,
137
+ & mut err,
138
+ ) ;
139
+
134
140
let mut is_loop_move = false ;
135
141
for move_site in & move_site_vec {
136
142
let move_out = self . move_data . moves [ ( * move_site) . moi ] ;
@@ -1056,16 +1062,118 @@ enum StorageDeadOrDrop<'tcx> {
1056
1062
}
1057
1063
1058
1064
impl < ' cx , ' gcx , ' tcx > MirBorrowckCtxt < ' cx , ' gcx , ' tcx > {
1059
- // End-user visible description of `place` if one can be found. If the
1060
- // place is a temporary for instance, None will be returned.
1065
+
1066
+ /// Adds a suggestion when a closure is invoked twice with a moved variable.
1067
+ ///
1068
+ /// ```text
1069
+ /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
1070
+ /// its environment
1071
+ /// --> $DIR/issue-42065.rs:16:29
1072
+ /// |
1073
+ /// LL | for (key, value) in dict {
1074
+ /// | ^^^^
1075
+ /// ```
1076
+ pub ( super ) fn add_closure_invoked_twice_with_moved_variable_suggestion (
1077
+ & self ,
1078
+ location : Location ,
1079
+ place : & Place < ' tcx > ,
1080
+ diag : & mut DiagnosticBuilder < ' _ > ,
1081
+ ) {
1082
+ let mut target = place. local ( ) ;
1083
+ debug ! (
1084
+ "add_closure_invoked_twice_with_moved_variable_suggestion: location={:?} place={:?} \
1085
+ target={:?}",
1086
+ location, place, target,
1087
+ ) ;
1088
+ for stmt in & self . mir [ location. block ] . statements [ location. statement_index ..] {
1089
+ debug ! (
1090
+ "add_closure_invoked_twice_with_moved_variable_suggestion: stmt={:?} \
1091
+ target={:?}",
1092
+ stmt, target,
1093
+ ) ;
1094
+ if let StatementKind :: Assign ( into, box Rvalue :: Use ( from) ) = & stmt. kind {
1095
+ debug ! (
1096
+ "add_closure_invoked_twice_with_moved_variable_suggestion: into={:?} \
1097
+ from={:?}",
1098
+ into, from,
1099
+ ) ;
1100
+ match from {
1101
+ Operand :: Copy ( ref place) |
1102
+ Operand :: Move ( ref place) if target == place. local ( ) =>
1103
+ target = into. local ( ) ,
1104
+ _ => { } ,
1105
+ }
1106
+ }
1107
+ }
1108
+
1109
+
1110
+ let terminator = self . mir [ location. block ] . terminator ( ) ;
1111
+ debug ! (
1112
+ "add_closure_invoked_twice_with_moved_variable_suggestion: terminator={:?}" ,
1113
+ terminator,
1114
+ ) ;
1115
+ if let TerminatorKind :: Call {
1116
+ func : Operand :: Constant ( box Constant {
1117
+ literal : ty:: Const { ty : & ty:: TyS { sty : ty:: TyKind :: FnDef ( id, _) , .. } , .. } ,
1118
+ ..
1119
+ } ) ,
1120
+ args,
1121
+ ..
1122
+ } = & terminator. kind {
1123
+ debug ! ( "add_closure_invoked_twice_with_moved_variable_suggestion: id={:?}" , id) ;
1124
+ if self . infcx . tcx . parent ( id) == self . infcx . tcx . lang_items ( ) . fn_once_trait ( ) {
1125
+ let closure = match args. first ( ) {
1126
+ Some ( Operand :: Copy ( ref place) ) |
1127
+ Some ( Operand :: Move ( ref place) ) if target == place. local ( ) =>
1128
+ place. local ( ) . unwrap ( ) ,
1129
+ _ => return ,
1130
+ } ;
1131
+ debug ! (
1132
+ "add_closure_invoked_twice_with_moved_variable_suggestion: closure={:?}" ,
1133
+ closure,
1134
+ ) ;
1135
+
1136
+ if let ty:: TyKind :: Closure ( did, substs) = self . mir . local_decls [ closure] . ty . sty {
1137
+ let upvar_tys = substs. upvar_tys ( did, self . infcx . tcx ) ;
1138
+ let node_id = match self . infcx . tcx . hir . as_local_node_id ( did) {
1139
+ Some ( node_id) => node_id,
1140
+ _ => return ,
1141
+ } ;
1142
+
1143
+ self . infcx . tcx . with_freevars ( node_id, |freevars| {
1144
+ for ( freevar, upvar_ty) in freevars. iter ( ) . zip ( upvar_tys) {
1145
+ debug ! (
1146
+ "add_closure_invoked_twice_with_moved_variable_suggestion: \
1147
+ freevar={:?} upvar_ty={:?}",
1148
+ freevar, upvar_ty,
1149
+ ) ;
1150
+ if !upvar_ty. is_region_ptr ( ) {
1151
+ diag. span_note (
1152
+ freevar. span ,
1153
+ & format ! (
1154
+ "closure cannot be invoked more than once because it \
1155
+ moves the variable `{}` out of its environment",
1156
+ self . infcx. tcx. hir. name( freevar. var_id( ) ) ,
1157
+ ) ,
1158
+ ) ;
1159
+ }
1160
+ }
1161
+ } ) ;
1162
+ }
1163
+ }
1164
+ }
1165
+ }
1166
+
1167
+ /// End-user visible description of `place` if one can be found. If the
1168
+ /// place is a temporary for instance, None will be returned.
1061
1169
pub ( super ) fn describe_place ( & self , place : & Place < ' tcx > ) -> Option < String > {
1062
1170
self . describe_place_with_options ( place, IncludingDowncast ( false ) )
1063
1171
}
1064
1172
1065
- // End-user visible description of `place` if one can be found. If the
1066
- // place is a temporary for instance, None will be returned.
1067
- // `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
1068
- // `Downcast` and `IncludingDowncast` is true
1173
+ /// End-user visible description of `place` if one can be found. If the
1174
+ /// place is a temporary for instance, None will be returned.
1175
+ /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
1176
+ /// `Downcast` and `IncludingDowncast` is true
1069
1177
pub ( super ) fn describe_place_with_options (
1070
1178
& self ,
1071
1179
place : & Place < ' tcx > ,
@@ -1078,7 +1186,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1078
1186
}
1079
1187
}
1080
1188
1081
- // Appends end-user visible description of `place` to `buf`.
1189
+ /// Appends end-user visible description of `place` to `buf`.
1082
1190
fn append_place_to_string (
1083
1191
& self ,
1084
1192
place : & Place < ' tcx > ,
@@ -1213,8 +1321,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1213
1321
Ok ( ( ) )
1214
1322
}
1215
1323
1216
- // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
1217
- // a name, then `Err` is returned
1324
+ /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
1325
+ /// a name, then `Err` is returned
1218
1326
fn append_local_to_string ( & self , local_index : Local , buf : & mut String ) -> Result < ( ) , ( ) > {
1219
1327
let local = & self . mir . local_decls [ local_index] ;
1220
1328
match local. name {
@@ -1226,7 +1334,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1226
1334
}
1227
1335
}
1228
1336
1229
- // End-user visible description of the `field`nth field of `base`
1337
+ /// End-user visible description of the `field`nth field of `base`
1230
1338
fn describe_field ( & self , base : & Place , field : Field ) -> String {
1231
1339
match * base {
1232
1340
Place :: Local ( local) => {
@@ -1251,7 +1359,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1251
1359
}
1252
1360
}
1253
1361
1254
- // End-user visible description of the `field_index`nth field of `ty`
1362
+ /// End-user visible description of the `field_index`nth field of `ty`
1255
1363
fn describe_field_from_ty ( & self , ty : & ty:: Ty , field : Field ) -> String {
1256
1364
if ty. is_box ( ) {
1257
1365
// If the type is a box, the field is described from the boxed type
@@ -1294,7 +1402,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1294
1402
}
1295
1403
}
1296
1404
1297
- // Retrieve type of a place for the current MIR representation
1405
+ /// Retrieve type of a place for the current MIR representation
1298
1406
fn retrieve_type_for_place ( & self , place : & Place < ' tcx > ) -> Option < ty:: Ty > {
1299
1407
match place {
1300
1408
Place :: Local ( local) => {
0 commit comments