Skip to content

Commit f1ccfe6

Browse files
committed
completly revised logic
1 parent 117bccf commit f1ccfe6

File tree

3 files changed

+64
-54
lines changed

3 files changed

+64
-54
lines changed

compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ 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-
/// Track whether we're currently inside a `&raw const/mut` expression.
49-
/// Used to allow safe access to union fields when only taking their address.
50-
in_raw_borrow: bool,
5148
}
5249

5350
impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
@@ -226,7 +223,6 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
226223
inside_adt: false,
227224
warnings: self.warnings,
228225
suggest_unsafe_block: self.suggest_unsafe_block,
229-
in_raw_borrow: false,
230226
};
231227
// params in THIR may be unsafe, e.g. a union pattern.
232228
for param in &inner_thir.params {
@@ -549,22 +545,28 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
549545
}
550546
}
551547
ExprKind::RawBorrow { arg, .. } => {
552-
// Set flag when entering raw borrow context
553-
let old_in_raw_borrow = self.in_raw_borrow;
554-
self.in_raw_borrow = is_direct_place_expr(self.thir, arg);
548+
// Handle the case where we're taking a raw pointer to a union field
549+
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind {
550+
if self.is_union_field_access(arg) {
551+
// Taking a raw pointer to a union field is safe - just check the base expression
552+
// but skip the union field safety check
553+
self.visit_union_field_for_raw_borrow(arg);
554+
return;
555+
}
556+
} else if self.is_union_field_access(arg) {
557+
// Direct raw borrow of union field
558+
self.visit_union_field_for_raw_borrow(arg);
559+
return;
560+
}
555561

556562
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
557563
&& let ExprKind::Deref { arg } = self.thir[arg].kind
558564
{
559565
// Taking a raw ref to a deref place expr is always safe.
560566
// Make sure the expression we're deref'ing is safe, though.
561567
visit::walk_expr(self, &self.thir[arg]);
568+
return;
562569
}
563-
564-
visit::walk_expr(self, &self.thir[arg]);
565-
// Restore previous state
566-
self.in_raw_borrow = old_in_raw_borrow;
567-
return;
568570
}
569571
ExprKind::Deref { arg } => {
570572
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
@@ -661,6 +663,8 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
661663
if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
662664
self.requires_unsafe(expr.span, UseOfUnsafeField);
663665
} else if adt_def.is_union() {
666+
// Check if this field access is part of a raw borrow operation
667+
// If so, we've already handled it above and shouldn't reach here
664668
if let Some(assigned_ty) = self.assignment_info {
665669
if assigned_ty.needs_drop(self.tcx, self.typing_env) {
666670
// This would be unsafe, but should be outright impossible since we
@@ -670,10 +674,8 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
670674
"union fields that need dropping should be impossible: {assigned_ty}"
671675
);
672676
}
673-
} else if !self.in_raw_borrow {
674-
// Union field access is unsafe because the field may be uninitialized.
675-
// However, `&raw const/mut union.field` is safe since it only computes
676-
// the field's address without reading the potentially uninitialized value.
677+
} else {
678+
// Only require unsafe if this is not a raw borrow operation
677679
self.requires_unsafe(expr.span, AccessToUnionField);
678680
}
679681
}
@@ -727,16 +729,43 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
727729
}
728730
}
729731

730-
fn is_direct_place_expr<'a, 'tcx>(thir: &'a Thir<'tcx>, expr_id: ExprId) -> bool {
731-
match thir[expr_id].kind {
732-
// Direct place expressions that don't read values
733-
ExprKind::Field { .. } | ExprKind::VarRef { .. } | ExprKind::UpvarRef { .. } => true,
734-
735-
// Scope is transparent for place expressions
736-
ExprKind::Scope { value, .. } => is_direct_place_expr(thir, value),
732+
impl<'a, 'tcx> UnsafetyVisitor<'a, 'tcx> {
733+
/// Check if an expression is a union field access
734+
fn is_union_field_access(&self, expr_id: ExprId) -> bool {
735+
match self.thir[expr_id].kind {
736+
ExprKind::Field { lhs, .. } => {
737+
let lhs = &self.thir[lhs];
738+
if let ty::Adt(adt_def, _) = lhs.ty.kind() { adt_def.is_union() } else { false }
739+
}
740+
_ => false,
741+
}
742+
}
737743

738-
// Any other expression (including Deref, method calls, etc.) reads values
739-
_ => false,
744+
/// Visit a union field access in the context of a raw borrow operation
745+
/// This ensures we still check safety of nested operations while allowing
746+
/// 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]);
762+
}
763+
}
764+
_ => {
765+
// Not a field access, use normal visiting
766+
visit::walk_expr(self, &self.thir[expr_id]);
767+
}
768+
}
740769
}
741770
}
742771

@@ -1227,7 +1256,6 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
12271256
inside_adt: false,
12281257
warnings: &mut warnings,
12291258
suggest_unsafe_block: true,
1230-
in_raw_borrow: false,
12311259
};
12321260
// params in THIR may be unsafe, e.g. a union pattern.
12331261
for param in &thir.params {

tests/ui/union/union-unsafe.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ fn deref_union_field(mut u: URef) {
3636
}
3737

3838
fn raw_deref_union_field(mut u: URef) {
39-
let _p = &raw const *(u.p);
40-
//~^ ERROR access to union field is unsafe
41-
//~| ERROR access to union field is unsafe
39+
// This is unsafe because we first dereference u.p (reading uninitialized memory)
40+
let _p = &raw const *(u.p); //~ ERROR access to union field is unsafe
4241
}
4342

4443
fn assign_noncopy_union_field(mut u: URefCell) {
@@ -77,8 +76,9 @@ fn main() {
7776

7877
let u4 = U5 { a: 2 };
7978
let vec = vec![1, 2, 3];
79+
// This is unsafe because we read u4.a (potentially uninitialized memory)
80+
// to use as an array index
8081
let _a = &raw const vec[u4.a]; //~ ERROR access to union field is unsafe
81-
//~^ ERROR access to union field is unsafe
8282

8383
let U1 { a } = u1; //~ ERROR access to union field is unsafe
8484
if let U1 { a: 12 } = u1 {} //~ ERROR access to union field is unsafe

tests/ui/union/union-unsafe.stderr

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,62 +7,44 @@ LL | *(u.p) = 13;
77
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
88

99
error[E0133]: access to union field is unsafe and requires unsafe function or block
10-
--> $DIR/union-unsafe.rs:39:26
10+
--> $DIR/union-unsafe.rs:40:26
1111
|
1212
LL | let _p = &raw const *(u.p);
1313
| ^^^^^ access to union field
1414
|
1515
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
1616

1717
error[E0133]: access to union field is unsafe and requires unsafe function or block
18-
--> $DIR/union-unsafe.rs:39:26
19-
|
20-
LL | let _p = &raw const *(u.p);
21-
| ^^^^^ access to union field
22-
|
23-
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
24-
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
25-
26-
error[E0133]: access to union field is unsafe and requires unsafe function or block
27-
--> $DIR/union-unsafe.rs:53:6
18+
--> $DIR/union-unsafe.rs:52:6
2819
|
2920
LL | *u3.a = T::default();
3021
| ^^^^ access to union field
3122
|
3223
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
3324

3425
error[E0133]: access to union field is unsafe and requires unsafe function or block
35-
--> $DIR/union-unsafe.rs:59:6
26+
--> $DIR/union-unsafe.rs:58:6
3627
|
3728
LL | *u3.a = T::default();
3829
| ^^^^ access to union field
3930
|
4031
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
4132

4233
error[E0133]: access to union field is unsafe and requires unsafe function or block
43-
--> $DIR/union-unsafe.rs:67:13
34+
--> $DIR/union-unsafe.rs:66:13
4435
|
4536
LL | let a = u1.a;
4637
| ^^^^ access to union field
4738
|
4839
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
4940

5041
error[E0133]: access to union field is unsafe and requires unsafe function or block
51-
--> $DIR/union-unsafe.rs:80:29
52-
|
53-
LL | let _a = &raw const vec[u4.a];
54-
| ^^^^ access to union field
55-
|
56-
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
57-
58-
error[E0133]: access to union field is unsafe and requires unsafe function or block
59-
--> $DIR/union-unsafe.rs:80:29
42+
--> $DIR/union-unsafe.rs:81:29
6043
|
6144
LL | let _a = &raw const vec[u4.a];
6245
| ^^^^ access to union field
6346
|
6447
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
65-
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
6648

6749
error[E0133]: access to union field is unsafe and requires unsafe function or block
6850
--> $DIR/union-unsafe.rs:83:14
@@ -112,6 +94,6 @@ LL | *u3.a = String::from("new");
11294
|
11395
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
11496

115-
error: aborting due to 14 previous errors
97+
error: aborting due to 12 previous errors
11698

11799
For more information about this error, try `rustc --explain E0133`.

0 commit comments

Comments
 (0)