@@ -5,7 +5,7 @@ use rustc_errors::struct_span_err;
5
5
use rustc_hir as hir;
6
6
use rustc_middle:: mir:: BorrowKind ;
7
7
use rustc_middle:: thir:: * ;
8
- use rustc_middle:: ty:: { self , ParamEnv , TyCtxt } ;
8
+ use rustc_middle:: ty:: { self , ParamEnv , Ty , TyCtxt } ;
9
9
use rustc_session:: lint:: builtin:: { UNSAFE_OP_IN_UNSAFE_FN , UNUSED_UNSAFE } ;
10
10
use rustc_session:: lint:: Level ;
11
11
use rustc_span:: def_id:: { DefId , LocalDefId } ;
@@ -27,7 +27,9 @@ struct UnsafetyVisitor<'a, 'tcx> {
27
27
/// The `#[target_feature]` attributes of the body. Used for checking
28
28
/// calls to functions with `#[target_feature]` (RFC 2396).
29
29
body_target_features : & ' tcx Vec < Symbol > ,
30
- in_possible_lhs_union_assign : bool ,
30
+ /// When inside the LHS of an assignment to a field, this is the type
31
+ /// of the LHS and the span of the assignment expression.
32
+ assignment_info : Option < ( Ty < ' tcx > , Span ) > ,
31
33
in_union_destructure : bool ,
32
34
param_env : ParamEnv < ' tcx > ,
33
35
inside_adt : bool ,
@@ -287,7 +289,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
287
289
}
288
290
289
291
fn visit_expr ( & mut self , expr : & Expr < ' tcx > ) {
290
- // could we be in a the LHS of an assignment of a union ?
292
+ // could we be in the LHS of an assignment to a field ?
291
293
match expr. kind {
292
294
ExprKind :: Field { .. }
293
295
| ExprKind :: VarRef { .. }
@@ -329,7 +331,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
329
331
| ExprKind :: InlineAsm { .. }
330
332
| ExprKind :: LlvmInlineAsm { .. }
331
333
| ExprKind :: LogicalOp { .. }
332
- | ExprKind :: Use { .. } => self . in_possible_lhs_union_assign = false ,
334
+ | ExprKind :: Use { .. } => {
335
+ // We don't need to save the old value and restore it
336
+ // because all the place expressions can't have more
337
+ // than one child.
338
+ self . assignment_info = None ;
339
+ }
333
340
} ;
334
341
match expr. kind {
335
342
ExprKind :: Scope { value, lint_level : LintLevel :: Explicit ( hir_id) , region_scope : _ } => {
@@ -409,32 +416,42 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
409
416
self . safety_context = closure_visitor. safety_context ;
410
417
}
411
418
ExprKind :: Field { lhs, .. } => {
412
- // assigning to union field is okay for AccessToUnionField
413
- if let ty:: Adt ( adt_def, _) = & self . thir [ lhs] . ty . kind ( ) {
419
+ let lhs = & self . thir [ lhs ] ;
420
+ if let ty:: Adt ( adt_def, _) = lhs. ty . kind ( ) {
414
421
if adt_def. is_union ( ) {
415
- if self . in_possible_lhs_union_assign {
416
- // FIXME: trigger AssignToDroppingUnionField unsafety if needed
422
+ if let Some ( ( assigned_ty, assignment_span) ) = self . assignment_info {
423
+ // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
424
+ if !( assigned_ty
425
+ . ty_adt_def ( )
426
+ . map_or ( false , |adt| adt. is_manually_drop ( ) )
427
+ || assigned_ty
428
+ . is_copy_modulo_regions ( self . tcx . at ( expr. span ) , self . param_env ) )
429
+ {
430
+ self . requires_unsafe ( assignment_span, AssignToDroppingUnionField ) ;
431
+ } else {
432
+ // write to non-drop union field, safe
433
+ }
417
434
} else {
418
435
self . requires_unsafe ( expr. span , AccessToUnionField ) ;
419
436
}
420
437
}
421
438
}
422
439
}
423
440
ExprKind :: Assign { lhs, rhs } | ExprKind :: AssignOp { lhs, rhs, .. } => {
441
+ let lhs = & self . thir [ lhs] ;
424
442
// First, check whether we are mutating a layout constrained field
425
443
let mut visitor = LayoutConstrainedPlaceVisitor :: new ( self . thir , self . tcx ) ;
426
- visit:: walk_expr ( & mut visitor, & self . thir [ lhs] ) ;
444
+ visit:: walk_expr ( & mut visitor, lhs) ;
427
445
if visitor. found {
428
446
self . requires_unsafe ( expr. span , MutationOfLayoutConstrainedField ) ;
429
447
}
430
448
431
449
// Second, check for accesses to union fields
432
450
// don't have any special handling for AssignOp since it causes a read *and* write to lhs
433
451
if matches ! ( expr. kind, ExprKind :: Assign { .. } ) {
434
- // assigning to a union is safe, check here so it doesn't get treated as a read later
435
- self . in_possible_lhs_union_assign = true ;
436
- visit:: walk_expr ( self , & self . thir ( ) [ lhs] ) ;
437
- self . in_possible_lhs_union_assign = false ;
452
+ self . assignment_info = Some ( ( lhs. ty , expr. span ) ) ;
453
+ visit:: walk_expr ( self , lhs) ;
454
+ self . assignment_info = None ;
438
455
visit:: walk_expr ( self , & self . thir ( ) [ rhs] ) ;
439
456
return ; // we have already visited everything by now
440
457
}
@@ -506,12 +523,9 @@ enum UnsafeOpKind {
506
523
UseOfMutableStatic ,
507
524
UseOfExternStatic ,
508
525
DerefOfRawPointer ,
509
- #[ allow( dead_code) ] // FIXME
510
526
AssignToDroppingUnionField ,
511
527
AccessToUnionField ,
512
- #[ allow( dead_code) ] // FIXME
513
528
MutationOfLayoutConstrainedField ,
514
- #[ allow( dead_code) ] // FIXME
515
529
BorrowOfLayoutConstrainedField ,
516
530
CallToFunctionWith ,
517
531
}
@@ -619,7 +633,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
619
633
hir_context : hir_id,
620
634
body_unsafety,
621
635
body_target_features,
622
- in_possible_lhs_union_assign : false ,
636
+ assignment_info : None ,
623
637
in_union_destructure : false ,
624
638
param_env : tcx. param_env ( def. did ) ,
625
639
inside_adt : false ,
0 commit comments