@@ -45,6 +45,8 @@ struct UnsafetyVisitor<'a, 'tcx> {
45
45
/// Flag to ensure that we only suggest wrapping the entire function body in
46
46
/// an unsafe block once.
47
47
suggest_unsafe_block : bool ,
48
+ /// Controls how union field accesses are checked
49
+ union_field_access_mode : UnionFieldAccessMode ,
48
50
}
49
51
50
52
impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
@@ -223,6 +225,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
223
225
inside_adt : false ,
224
226
warnings : self . warnings ,
225
227
suggest_unsafe_block : self . suggest_unsafe_block ,
228
+ union_field_access_mode : UnionFieldAccessMode :: Normal ,
226
229
} ;
227
230
// params in THIR may be unsafe, e.g. a union pattern.
228
231
for param in & inner_thir. params {
@@ -665,18 +668,25 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
665
668
} else if adt_def. is_union ( ) {
666
669
// Check if this field access is part of a raw borrow operation
667
670
// If so, we've already handled it above and shouldn't reach here
668
- if let Some ( assigned_ty) = self . assignment_info {
669
- if assigned_ty. needs_drop ( self . tcx , self . typing_env ) {
670
- // This would be unsafe, but should be outright impossible since we
671
- // reject such unions.
672
- assert ! (
673
- self . tcx. dcx( ) . has_errors( ) . is_some( ) ,
674
- "union fields that need dropping should be impossible: {assigned_ty}"
675
- ) ;
671
+ match self . union_field_access_mode {
672
+ UnionFieldAccessMode :: SuppressUnionFieldAccessError => {
673
+ // Suppress AccessToUnionField error for union fields chains
674
+ }
675
+ UnionFieldAccessMode :: Normal => {
676
+ if let Some ( assigned_ty) = self . assignment_info {
677
+ if assigned_ty. needs_drop ( self . tcx , self . typing_env ) {
678
+ // This would be unsafe, but should be outright impossible since we
679
+ // reject such unions.
680
+ assert ! (
681
+ self . tcx. dcx( ) . has_errors( ) . is_some( ) ,
682
+ "union fields that need dropping should be impossible: {assigned_ty}"
683
+ ) ;
684
+ }
685
+ } else {
686
+ // Only require unsafe if this is not a raw borrow operation
687
+ self . requires_unsafe ( expr. span , AccessToUnionField ) ;
688
+ }
676
689
}
677
- } else {
678
- // Only require unsafe if this is not a raw borrow operation
679
- self . requires_unsafe ( expr. span , AccessToUnionField ) ;
680
690
}
681
691
}
682
692
}
@@ -735,7 +745,7 @@ impl<'a, 'tcx> UnsafetyVisitor<'a, 'tcx> {
735
745
match self . thir [ expr_id] . kind {
736
746
ExprKind :: Field { lhs, .. } => {
737
747
let lhs = & self . thir [ lhs] ;
738
- if let ty:: Adt ( adt_def, _) = lhs . ty . kind ( ) { adt_def. is_union ( ) } else { false }
748
+ matches ! ( lhs . ty . kind ( ) , ty:: Adt ( adt_def, _) if adt_def. is_union( ) )
739
749
}
740
750
_ => false ,
741
751
}
@@ -744,28 +754,28 @@ impl<'a, 'tcx> UnsafetyVisitor<'a, 'tcx> {
744
754
/// Visit a union field access in the context of a raw borrow operation
745
755
/// This ensures we still check safety of nested operations while allowing
746
756
/// the raw pointer creation itself
747
- fn visit_union_field_for_raw_borrow ( & mut self , expr_id : ExprId ) {
748
- match self . thir [ expr_id] . kind {
749
- ExprKind :: Field { lhs, variant_index, name } => {
750
- let lhs_expr = & self . thir [ lhs] ;
751
- if let ty:: Adt ( adt_def, _) = lhs_expr. ty . kind ( ) {
752
- // Check for unsafe fields but skip the union access check
753
- if adt_def. variant ( variant_index) . fields [ name] . safety . is_unsafe ( ) {
754
- self . requires_unsafe ( self . thir [ expr_id] . span , UseOfUnsafeField ) ;
755
- }
756
- // For unions, we don't require unsafe for raw pointer creation
757
- // But we still need to check the LHS for safety
758
- self . visit_expr ( lhs_expr) ;
759
- } else {
760
- // Not a union, use normal visiting
761
- visit:: walk_expr ( self , & self . thir [ expr_id] ) ;
757
+ fn visit_union_field_for_raw_borrow ( & mut self , mut expr_id : ExprId ) {
758
+ let prev = self . union_field_access_mode ;
759
+ self . union_field_access_mode = UnionFieldAccessMode :: SuppressUnionFieldAccessError ;
760
+ // Walk through the chain of union field accesses using while let
761
+ while let ExprKind :: Field { lhs, variant_index, name } = self . thir [ expr_id] . kind {
762
+ let lhs_expr = & self . thir [ lhs] ;
763
+ if let ty:: Adt ( adt_def, _) = lhs_expr. ty . kind ( ) {
764
+ // Check for unsafe fields but skip the union access check
765
+ if adt_def. variant ( variant_index) . fields [ name] . safety . is_unsafe ( ) {
766
+ self . requires_unsafe ( self . thir [ expr_id] . span , UseOfUnsafeField ) ;
762
767
}
763
- }
764
- _ => {
765
- // Not a field access, use normal visiting
768
+ // If the LHS is also a union field access, keep walking
769
+ expr_id = lhs;
770
+ } else {
771
+ // Not a union, use normal visiting
766
772
visit:: walk_expr ( self , & self . thir [ expr_id] ) ;
773
+ return ;
767
774
}
768
775
}
776
+ // Visit the base expression for any nested safety checks
777
+ self . visit_expr ( & self . thir [ expr_id] ) ;
778
+ self . union_field_access_mode = prev;
769
779
}
770
780
}
771
781
@@ -777,6 +787,13 @@ enum SafetyContext {
777
787
UnsafeBlock { span : Span , hir_id : HirId , used : bool , nested_used_blocks : Vec < NestedUsedBlock > } ,
778
788
}
779
789
790
+ /// Controls how union field accesses are checked
791
+ #[ derive( Clone , Copy ) ]
792
+ enum UnionFieldAccessMode {
793
+ Normal ,
794
+ SuppressUnionFieldAccessError ,
795
+ }
796
+
780
797
#[ derive( Clone , Copy ) ]
781
798
struct NestedUsedBlock {
782
799
hir_id : HirId ,
@@ -1256,6 +1273,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
1256
1273
inside_adt : false ,
1257
1274
warnings : & mut warnings,
1258
1275
suggest_unsafe_block : true ,
1276
+ union_field_access_mode : UnionFieldAccessMode :: Normal ,
1259
1277
} ;
1260
1278
// params in THIR may be unsafe, e.g. a union pattern.
1261
1279
for param in & thir. params {
0 commit comments