@@ -45,6 +45,8 @@ struct UnsafetyVisitor<'a, 'tcx> {
4545 /// Flag to ensure that we only suggest wrapping the entire function body in
4646 /// an unsafe block once.
4747 suggest_unsafe_block : bool ,
48+ /// Controls how union field accesses are checked
49+ union_field_access_mode : UnionFieldAccessMode ,
4850}
4951
5052impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
@@ -223,6 +225,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
223225 inside_adt : false ,
224226 warnings : self . warnings ,
225227 suggest_unsafe_block : self . suggest_unsafe_block ,
228+ union_field_access_mode : UnionFieldAccessMode :: Normal ,
226229 } ;
227230 // params in THIR may be unsafe, e.g. a union pattern.
228231 for param in & inner_thir. params {
@@ -665,18 +668,25 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
665668 } else if adt_def. is_union ( ) {
666669 // Check if this field access is part of a raw borrow operation
667670 // 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+ }
676689 }
677- } else {
678- // Only require unsafe if this is not a raw borrow operation
679- self . requires_unsafe ( expr. span , AccessToUnionField ) ;
680690 }
681691 }
682692 }
@@ -735,7 +745,7 @@ impl<'a, 'tcx> UnsafetyVisitor<'a, 'tcx> {
735745 match self . thir [ expr_id] . kind {
736746 ExprKind :: Field { lhs, .. } => {
737747 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( ) )
739749 }
740750 _ => false ,
741751 }
@@ -744,28 +754,28 @@ impl<'a, 'tcx> UnsafetyVisitor<'a, 'tcx> {
744754 /// Visit a union field access in the context of a raw borrow operation
745755 /// This ensures we still check safety of nested operations while allowing
746756 /// 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 ) ;
762767 }
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
766772 visit:: walk_expr ( self , & self . thir [ expr_id] ) ;
773+ return ;
767774 }
768775 }
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;
769779 }
770780}
771781
@@ -777,6 +787,13 @@ enum SafetyContext {
777787 UnsafeBlock { span : Span , hir_id : HirId , used : bool , nested_used_blocks : Vec < NestedUsedBlock > } ,
778788}
779789
790+ /// Controls how union field accesses are checked
791+ #[ derive( Clone , Copy ) ]
792+ enum UnionFieldAccessMode {
793+ Normal ,
794+ SuppressUnionFieldAccessError ,
795+ }
796+
780797#[ derive( Clone , Copy ) ]
781798struct NestedUsedBlock {
782799 hir_id : HirId ,
@@ -1256,6 +1273,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
12561273 inside_adt : false ,
12571274 warnings : & mut warnings,
12581275 suggest_unsafe_block : true ,
1276+ union_field_access_mode : UnionFieldAccessMode :: Normal ,
12591277 } ;
12601278 // params in THIR may be unsafe, e.g. a union pattern.
12611279 for param in & thir. params {
0 commit comments