@@ -5,7 +5,7 @@ use rustc_errors::struct_span_err;
55use rustc_hir as hir;
66use rustc_middle:: mir:: BorrowKind ;
77use rustc_middle:: thir:: * ;
8- use rustc_middle:: ty:: { self , ParamEnv , TyCtxt } ;
8+ use rustc_middle:: ty:: { self , ParamEnv , Ty , TyCtxt } ;
99use rustc_session:: lint:: builtin:: { UNSAFE_OP_IN_UNSAFE_FN , UNUSED_UNSAFE } ;
1010use rustc_session:: lint:: Level ;
1111use rustc_span:: def_id:: { DefId , LocalDefId } ;
@@ -27,7 +27,9 @@ struct UnsafetyVisitor<'a, 'tcx> {
2727 /// The `#[target_feature]` attributes of the body. Used for checking
2828 /// calls to functions with `#[target_feature]` (RFC 2396).
2929 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 ) > ,
3133 in_union_destructure : bool ,
3234 param_env : ParamEnv < ' tcx > ,
3335 inside_adt : bool ,
@@ -287,7 +289,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
287289 }
288290
289291 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 ?
291293 match expr. kind {
292294 ExprKind :: Field { .. }
293295 | ExprKind :: VarRef { .. }
@@ -329,7 +331,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
329331 | ExprKind :: InlineAsm { .. }
330332 | ExprKind :: LlvmInlineAsm { .. }
331333 | 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+ }
333340 } ;
334341 match expr. kind {
335342 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> {
409416 self . safety_context = closure_visitor. safety_context ;
410417 }
411418 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 ( ) {
414421 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+ }
417434 } else {
418435 self . requires_unsafe ( expr. span , AccessToUnionField ) ;
419436 }
420437 }
421438 }
422439 }
423440 ExprKind :: Assign { lhs, rhs } | ExprKind :: AssignOp { lhs, rhs, .. } => {
441+ let lhs = & self . thir [ lhs] ;
424442 // First, check whether we are mutating a layout constrained field
425443 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) ;
427445 if visitor. found {
428446 self . requires_unsafe ( expr. span , MutationOfLayoutConstrainedField ) ;
429447 }
430448
431449 // Second, check for accesses to union fields
432450 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
433451 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 ;
438455 visit:: walk_expr ( self , & self . thir ( ) [ rhs] ) ;
439456 return ; // we have already visited everything by now
440457 }
@@ -506,12 +523,9 @@ enum UnsafeOpKind {
506523 UseOfMutableStatic ,
507524 UseOfExternStatic ,
508525 DerefOfRawPointer ,
509- #[ allow( dead_code) ] // FIXME
510526 AssignToDroppingUnionField ,
511527 AccessToUnionField ,
512- #[ allow( dead_code) ] // FIXME
513528 MutationOfLayoutConstrainedField ,
514- #[ allow( dead_code) ] // FIXME
515529 BorrowOfLayoutConstrainedField ,
516530 CallToFunctionWith ,
517531}
@@ -619,7 +633,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
619633 hir_context : hir_id,
620634 body_unsafety,
621635 body_target_features,
622- in_possible_lhs_union_assign : false ,
636+ assignment_info : None ,
623637 in_union_destructure : false ,
624638 param_env : tcx. param_env ( def. did ) ,
625639 inside_adt : false ,
0 commit comments