Skip to content

Commit 43892d3

Browse files
committed
completly revised logic
1 parent 87fb4ad commit 43892d3

File tree

4 files changed

+69
-59
lines changed

4 files changed

+69
-59
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> {
@@ -221,7 +218,6 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
221218
inside_adt: false,
222219
warnings: self.warnings,
223220
suggest_unsafe_block: self.suggest_unsafe_block,
224-
in_raw_borrow: false,
225221
};
226222
// params in THIR may be unsafe, e.g. a union pattern.
227223
for param in &inner_thir.params {
@@ -542,22 +538,28 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
542538
}
543539
}
544540
ExprKind::RawBorrow { arg, .. } => {
545-
// Set flag when entering raw borrow context
546-
let old_in_raw_borrow = self.in_raw_borrow;
547-
self.in_raw_borrow = is_direct_place_expr(self.thir, arg);
541+
// Handle the case where we're taking a raw pointer to a union field
542+
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind {
543+
if self.is_union_field_access(arg) {
544+
// Taking a raw pointer to a union field is safe - just check the base expression
545+
// but skip the union field safety check
546+
self.visit_union_field_for_raw_borrow(arg);
547+
return;
548+
}
549+
} else if self.is_union_field_access(arg) {
550+
// Direct raw borrow of union field
551+
self.visit_union_field_for_raw_borrow(arg);
552+
return;
553+
}
548554

549555
if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
550556
&& let ExprKind::Deref { arg } = self.thir[arg].kind
551557
{
552558
// Taking a raw ref to a deref place expr is always safe.
553559
// Make sure the expression we're deref'ing is safe, though.
554560
visit::walk_expr(self, &self.thir[arg]);
561+
return;
555562
}
556-
557-
visit::walk_expr(self, &self.thir[arg]);
558-
// Restore previous state
559-
self.in_raw_borrow = old_in_raw_borrow;
560-
return;
561563
}
562564
ExprKind::Deref { arg } => {
563565
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
@@ -654,6 +656,8 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
654656
if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
655657
self.requires_unsafe(expr.span, UseOfUnsafeField);
656658
} else if adt_def.is_union() {
659+
// Check if this field access is part of a raw borrow operation
660+
// If so, we've already handled it above and shouldn't reach here
657661
if let Some(assigned_ty) = self.assignment_info {
658662
if assigned_ty.needs_drop(self.tcx, self.typing_env) {
659663
// This would be unsafe, but should be outright impossible since we
@@ -663,10 +667,8 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
663667
"union fields that need dropping should be impossible: {assigned_ty}"
664668
);
665669
}
666-
} else if !self.in_raw_borrow {
667-
// Union field access is unsafe because the field may be uninitialized.
668-
// However, `&raw const/mut union.field` is safe since it only computes
669-
// the field's address without reading the potentially uninitialized value.
670+
} else {
671+
// Only require unsafe if this is not a raw borrow operation
670672
self.requires_unsafe(expr.span, AccessToUnionField);
671673
}
672674
}
@@ -720,16 +722,43 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
720722
}
721723
}
722724

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

731-
// Any other expression (including Deref, method calls, etc.) reads values
732-
_ => false,
737+
/// Visit a union field access in the context of a raw borrow operation
738+
/// This ensures we still check safety of nested operations while allowing
739+
/// the raw pointer creation itself
740+
fn visit_union_field_for_raw_borrow(&mut self, expr_id: ExprId) {
741+
match self.thir[expr_id].kind {
742+
ExprKind::Field { lhs, variant_index, name } => {
743+
let lhs_expr = &self.thir[lhs];
744+
if let ty::Adt(adt_def, _) = lhs_expr.ty.kind() {
745+
// Check for unsafe fields but skip the union access check
746+
if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
747+
self.requires_unsafe(self.thir[expr_id].span, UseOfUnsafeField);
748+
}
749+
// For unions, we don't require unsafe for raw pointer creation
750+
// But we still need to check the LHS for safety
751+
self.visit_expr(lhs_expr);
752+
} else {
753+
// Not a union, use normal visiting
754+
visit::walk_expr(self, &self.thir[expr_id]);
755+
}
756+
}
757+
_ => {
758+
// Not a field access, use normal visiting
759+
visit::walk_expr(self, &self.thir[expr_id]);
760+
}
761+
}
733762
}
734763
}
735764

@@ -1215,7 +1244,6 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
12151244
inside_adt: false,
12161245
warnings: &mut warnings,
12171246
suggest_unsafe_block: true,
1218-
in_raw_borrow: false,
12191247
};
12201248
// params in THIR may be unsafe, e.g. a union pattern.
12211249
for param in &thir.params {

main

810 KB
Binary file not shown.

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: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,103 +15,85 @@ LL | let _p = &raw const *(u.p);
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:51: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:57: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:65: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:78: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
68-
--> $DIR/union-unsafe.rs:83:14
50+
--> $DIR/union-unsafe.rs:80:14
6951
|
7052
LL | let U1 { a } = u1;
7153
| ^ access to union field
7254
|
7355
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
7456

7557
error[E0133]: access to union field is unsafe and requires unsafe function or block
76-
--> $DIR/union-unsafe.rs:84:20
58+
--> $DIR/union-unsafe.rs:81:20
7759
|
7860
LL | if let U1 { a: 12 } = u1 {}
7961
| ^^ access to union field
8062
|
8163
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
8264

8365
error[E0133]: access to union field is unsafe and requires unsafe function or block
84-
--> $DIR/union-unsafe.rs:85:25
66+
--> $DIR/union-unsafe.rs:82:25
8567
|
8668
LL | if let Some(U1 { a: 13 }) = Some(u1) {}
8769
| ^^ access to union field
8870
|
8971
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
9072

9173
error[E0133]: access to union field is unsafe and requires unsafe function or block
92-
--> $DIR/union-unsafe.rs:90:6
74+
--> $DIR/union-unsafe.rs:87:6
9375
|
9476
LL | *u2.a = String::from("new");
9577
| ^^^^ access to union field
9678
|
9779
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
9880

9981
error[E0133]: access to union field is unsafe and requires unsafe function or block
100-
--> $DIR/union-unsafe.rs:94:6
82+
--> $DIR/union-unsafe.rs:91:6
10183
|
10284
LL | *u3.a = 1;
10385
| ^^^^ access to union field
10486
|
10587
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
10688

10789
error[E0133]: access to union field is unsafe and requires unsafe function or block
108-
--> $DIR/union-unsafe.rs:98:6
90+
--> $DIR/union-unsafe.rs:95:6
10991
|
11092
LL | *u3.a = String::from("new");
11193
| ^^^^ access to union field
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)