@@ -181,6 +181,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
181
181
self . check_mut_borrowing_layout_constrained_field ( * place, context. is_mutating_use ( ) ) ;
182
182
}
183
183
184
+ // Check for borrows to packed fields.
185
+ // `is_disaligned` already traverses the place to consider all projections after the last
186
+ // `Deref`, so this only needs to be called once at the top level.
184
187
if context. is_borrow ( ) {
185
188
if util:: is_disaligned ( self . tcx , self . body , self . param_env , * place) {
186
189
self . require_unsafe (
@@ -190,99 +193,105 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
190
193
}
191
194
}
192
195
193
- for ( i, _elem) in place. projection . iter ( ) . enumerate ( ) {
194
- let proj_base = & place. projection [ ..i] ;
195
- if context. is_borrow ( ) {
196
- if util:: is_disaligned ( self . tcx , self . body , self . param_env , * place) {
196
+ // Some checks below need the extra metainfo of the local declaration.
197
+ let decl = & self . body . local_decls [ place. local ] ;
198
+
199
+ // Check the base local: it might be an unsafe-to-access static. We only check derefs of the
200
+ // temporary holding the static pointer to avoid duplicate errors
201
+ // <https://github.com/rust-lang/rust/pull/78068#issuecomment-731753506>.
202
+ if decl. internal && place. projection . first ( ) == Some ( & ProjectionElem :: Deref ) {
203
+ // If the projection root is an artifical local that we introduced when
204
+ // desugaring `static`, give a more specific error message
205
+ // (avoid the general "raw pointer" clause below, that would only be confusing).
206
+ if let Some ( box LocalInfo :: StaticRef { def_id, .. } ) = decl. local_info {
207
+ if self . tcx . is_mutable_static ( def_id) {
197
208
self . require_unsafe (
198
- UnsafetyViolationKind :: BorrowPacked ,
199
- UnsafetyViolationDetails :: BorrowOfPackedField ,
209
+ UnsafetyViolationKind :: General ,
210
+ UnsafetyViolationDetails :: UseOfMutableStatic ,
200
211
) ;
212
+ return ;
213
+ } else if self . tcx . is_foreign_item ( def_id) {
214
+ self . require_unsafe (
215
+ UnsafetyViolationKind :: General ,
216
+ UnsafetyViolationDetails :: UseOfExternStatic ,
217
+ ) ;
218
+ return ;
201
219
}
202
220
}
203
- let source_info = self . source_info ;
204
- if let [ ] = proj_base {
205
- let decl = & self . body . local_decls [ place. local ] ;
206
- if decl. internal {
207
- // If the projection root is an artifical local that we introduced when
208
- // desugaring `static`, give a more specific error message
209
- // (avoid the general "raw pointer" clause below, that would only be confusing).
210
- if let Some ( box LocalInfo :: StaticRef { def_id, .. } ) = decl. local_info {
211
- if self . tcx . is_mutable_static ( def_id) {
212
- self . require_unsafe (
213
- UnsafetyViolationKind :: General ,
214
- UnsafetyViolationDetails :: UseOfMutableStatic ,
215
- ) ;
216
- return ;
217
- } else if self . tcx . is_foreign_item ( def_id) {
218
- self . require_unsafe (
219
- UnsafetyViolationKind :: General ,
220
- UnsafetyViolationDetails :: UseOfExternStatic ,
221
- ) ;
222
- return ;
223
- }
224
- } else {
225
- // Internal locals are used in the `move_val_init` desugaring.
226
- // We want to check unsafety against the source info of the
227
- // desugaring, rather than the source info of the RHS.
228
- self . source_info = self . body . local_decls [ place. local ] . source_info ;
229
- }
221
+ }
222
+
223
+ // Check for raw pointer `Deref`.
224
+ for ( base, proj) in place. iter_projections ( ) {
225
+ if proj == ProjectionElem :: Deref {
226
+ let source_info = self . source_info ; // Backup source_info so we can restore it later.
227
+ if base. projection . is_empty ( ) && decl. internal {
228
+ // Internal locals are used in the `move_val_init` desugaring.
229
+ // We want to check unsafety against the source info of the
230
+ // desugaring, rather than the source info of the RHS.
231
+ self . source_info = self . body . local_decls [ place. local ] . source_info ;
230
232
}
233
+ let base_ty = base. ty ( self . body , self . tcx ) . ty ;
234
+ if base_ty. is_unsafe_ptr ( ) {
235
+ self . require_unsafe (
236
+ UnsafetyViolationKind :: GeneralAndConstFn ,
237
+ UnsafetyViolationDetails :: DerefOfRawPointer ,
238
+ )
239
+ }
240
+ self . source_info = source_info; // Restore backed-up source_info.
241
+ }
242
+ }
243
+
244
+ // Check for union fields. For this we traverse right-to-left, as the last `Deref` changes
245
+ // whether we *read* the union field or potentially *write* to it (if this place is being assigned to).
246
+ let mut saw_deref = false ;
247
+ for ( base, proj) in place. iter_projections ( ) . rev ( ) {
248
+ if proj == ProjectionElem :: Deref {
249
+ saw_deref = true ;
250
+ continue ;
231
251
}
232
- let base_ty = Place :: ty_from ( place. local , proj_base, self . body , self . tcx ) . ty ;
233
- match base_ty. kind ( ) {
234
- ty:: RawPtr ( ..) => self . require_unsafe (
235
- UnsafetyViolationKind :: GeneralAndConstFn ,
236
- UnsafetyViolationDetails :: DerefOfRawPointer ,
237
- ) ,
238
- ty:: Adt ( adt, _) if adt. is_union ( ) => {
239
- let assign_to_field = matches ! (
252
+
253
+ let base_ty = base. ty ( self . body , self . tcx ) . ty ;
254
+ if base_ty. ty_adt_def ( ) . map_or ( false , |adt| adt. is_union ( ) ) {
255
+ // If we did not hit a `Deref` yet and the overall place use is an assignment, the
256
+ // rules are different.
257
+ let assign_to_field = !saw_deref
258
+ && matches ! (
240
259
context,
241
260
PlaceContext :: MutatingUse (
242
261
MutatingUseContext :: Store
243
262
| MutatingUseContext :: Drop
244
263
| MutatingUseContext :: AsmOutput
245
264
)
246
265
) ;
247
- // If there is a `Deref` further along the projection chain, this is *not* an
248
- // assignment to a union field. In that case the union field is just read to
249
- // obtain the pointer/reference.
250
- let assign_to_field = assign_to_field
251
- && !place. projection [ i..]
252
- . iter ( )
253
- . any ( |elem| matches ! ( elem, ProjectionElem :: Deref ) ) ;
254
- // If this is just an assignment, determine if the assigned type needs dropping.
255
- if assign_to_field {
256
- // We have to check the actual type of the assignment, as that determines if the
257
- // old value is being dropped.
258
- let assigned_ty = place. ty ( & self . body . local_decls , self . tcx ) . ty ;
259
- // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
260
- let manually_drop = assigned_ty
261
- . ty_adt_def ( )
262
- . map_or ( false , |adt_def| adt_def. is_manually_drop ( ) ) ;
263
- let nodrop = manually_drop
264
- || assigned_ty. is_copy_modulo_regions (
265
- self . tcx . at ( self . source_info . span ) ,
266
- self . param_env ,
267
- ) ;
268
- if !nodrop {
269
- self . require_unsafe (
270
- UnsafetyViolationKind :: GeneralAndConstFn ,
271
- UnsafetyViolationDetails :: AssignToDroppingUnionField ,
272
- ) ;
273
- } else {
274
- // write to non-drop union field, safe
275
- }
276
- } else {
266
+ // If this is just an assignment, determine if the assigned type needs dropping.
267
+ if assign_to_field {
268
+ // We have to check the actual type of the assignment, as that determines if the
269
+ // old value is being dropped.
270
+ let assigned_ty = place. ty ( & self . body . local_decls , self . tcx ) . ty ;
271
+ // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
272
+ let manually_drop = assigned_ty
273
+ . ty_adt_def ( )
274
+ . map_or ( false , |adt_def| adt_def. is_manually_drop ( ) ) ;
275
+ let nodrop = manually_drop
276
+ || assigned_ty. is_copy_modulo_regions (
277
+ self . tcx . at ( self . source_info . span ) ,
278
+ self . param_env ,
279
+ ) ;
280
+ if !nodrop {
277
281
self . require_unsafe (
278
282
UnsafetyViolationKind :: GeneralAndConstFn ,
279
- UnsafetyViolationDetails :: AccessToUnionField ,
280
- )
283
+ UnsafetyViolationDetails :: AssignToDroppingUnionField ,
284
+ ) ;
285
+ } else {
286
+ // write to non-drop union field, safe
281
287
}
288
+ } else {
289
+ self . require_unsafe (
290
+ UnsafetyViolationKind :: GeneralAndConstFn ,
291
+ UnsafetyViolationDetails :: AccessToUnionField ,
292
+ )
282
293
}
283
- _ => { }
284
294
}
285
- self . source_info = source_info;
286
295
}
287
296
}
288
297
}
0 commit comments