@@ -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 {
@@ -545,6 +548,20 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
545548 }
546549 }
547550 ExprKind :: RawBorrow { arg, .. } => {
551+ // Handle the case where we're taking a raw pointer to a union field
552+ if let ExprKind :: Scope { value : arg, .. } = self . thir [ arg] . kind {
553+ if self . is_union_field_access ( arg) {
554+ // Taking a raw pointer to a union field is safe - just check the base expression
555+ // but skip the union field safety check
556+ self . visit_union_field_for_raw_borrow ( arg) ;
557+ return ;
558+ }
559+ } else if self . is_union_field_access ( arg) {
560+ // Direct raw borrow of union field
561+ self . visit_union_field_for_raw_borrow ( arg) ;
562+ return ;
563+ }
564+
548565 if let ExprKind :: Scope { value : arg, .. } = self . thir [ arg] . kind
549566 && let ExprKind :: Deref { arg } = self . thir [ arg] . kind
550567 {
@@ -649,17 +666,27 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
649666 if adt_def. variant ( variant_index) . fields [ name] . safety . is_unsafe ( ) {
650667 self . requires_unsafe ( expr. span , UseOfUnsafeField ) ;
651668 } else if adt_def. is_union ( ) {
652- if let Some ( assigned_ty) = self . assignment_info {
653- if assigned_ty. needs_drop ( self . tcx , self . typing_env ) {
654- // This would be unsafe, but should be outright impossible since we
655- // reject such unions.
656- assert ! (
657- self . tcx. dcx( ) . has_errors( ) . is_some( ) ,
658- "union fields that need dropping should be impossible: {assigned_ty}"
659- ) ;
669+ // Check if this field access is part of a raw borrow operation
670+ // If so, we've already handled it above and shouldn't reach here
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+ }
660689 }
661- } else {
662- self . requires_unsafe ( expr. span , AccessToUnionField ) ;
663690 }
664691 }
665692 }
@@ -712,6 +739,46 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
712739 }
713740}
714741
742+ impl < ' a , ' tcx > UnsafetyVisitor < ' a , ' tcx > {
743+ /// Check if an expression is a union field access
744+ fn is_union_field_access ( & self , expr_id : ExprId ) -> bool {
745+ match self . thir [ expr_id] . kind {
746+ ExprKind :: Field { lhs, .. } => {
747+ let lhs = & self . thir [ lhs] ;
748+ matches ! ( lhs. ty. kind( ) , ty:: Adt ( adt_def, _) if adt_def. is_union( ) )
749+ }
750+ _ => false ,
751+ }
752+ }
753+
754+ /// Visit a union field access in the context of a raw borrow operation
755+ /// This ensures we still check safety of nested operations while allowing
756+ /// the raw pointer creation itself
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 ) ;
767+ }
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
772+ visit:: walk_expr ( self , & self . thir [ expr_id] ) ;
773+ return ;
774+ }
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;
779+ }
780+ }
781+
715782#[ derive( Clone ) ]
716783enum SafetyContext {
717784 Safe ,
@@ -720,6 +787,13 @@ enum SafetyContext {
720787 UnsafeBlock { span : Span , hir_id : HirId , used : bool , nested_used_blocks : Vec < NestedUsedBlock > } ,
721788}
722789
790+ /// Controls how union field accesses are checked
791+ #[ derive( Clone , Copy ) ]
792+ enum UnionFieldAccessMode {
793+ Normal ,
794+ SuppressUnionFieldAccessError ,
795+ }
796+
723797#[ derive( Clone , Copy ) ]
724798struct NestedUsedBlock {
725799 hir_id : HirId ,
@@ -1199,6 +1273,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
11991273 inside_adt : false ,
12001274 warnings : & mut warnings,
12011275 suggest_unsafe_block : true ,
1276+ union_field_access_mode : UnionFieldAccessMode :: Normal ,
12021277 } ;
12031278 // params in THIR may be unsafe, e.g. a union pattern.
12041279 for param in & thir. params {
0 commit comments