Skip to content

Commit 49ba112

Browse files
committed
fixed fields chain, added new test cases
1 parent f1ccfe6 commit 49ba112

File tree

3 files changed

+83
-31
lines changed

3 files changed

+83
-31
lines changed

compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -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

5052
impl<'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)]
781798
struct 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 {

src/tools/miri/tests/pass/both_borrows/smallvec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl<T, const N: usize> RawSmallVec<T, N> {
2525
}
2626

2727
const fn as_mut_ptr_inline(&mut self) -> *mut T {
28-
(unsafe { &raw mut self.inline }) as *mut T
28+
&raw mut self.inline as *mut T
2929
}
3030

3131
const unsafe fn as_mut_ptr_heap(&mut self) -> *mut T {

tests/ui/union/union-unsafe.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,38 @@ fn main() {
9696
let mut u3 = U3 { a: ManuallyDrop::new(String::from("old")) }; // OK
9797
u3.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop)
9898
*u3.a = String::from("new"); //~ ERROR access to union field is unsafe
99+
100+
let mut unions = [U1 { a: 1 }, U1 { a: 2 }];
101+
102+
// Array indexing + union field raw borrow - should be OK
103+
let ptr = &raw mut unions[0].a; // OK
104+
let ptr2 = &raw const unions[1].a; // OK
105+
106+
// Test for union fields chain, this should be allowed
107+
#[derive(Copy, Clone)]
108+
union Inner {
109+
a: u8,
110+
}
111+
112+
union MoreInner {
113+
moreinner: ManuallyDrop<Inner>,
114+
}
115+
116+
union LessOuter {
117+
lessouter: ManuallyDrop<MoreInner>,
118+
}
119+
120+
union Outer {
121+
outer: ManuallyDrop<LessOuter>,
122+
}
123+
124+
let super_outer = Outer {
125+
outer: ManuallyDrop::new(LessOuter {
126+
lessouter: ManuallyDrop::new(MoreInner {
127+
moreinner: ManuallyDrop::new(Inner { a: 42 }),
128+
}),
129+
}),
130+
};
131+
132+
let ptr = &raw const super_outer.outer.lessouter.moreinner.a;
99133
}

0 commit comments

Comments
 (0)