Skip to content

Allow &raw [mut | const] for union field in safe #141469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ struct UnsafetyVisitor<'a, 'tcx> {
/// Flag to ensure that we only suggest wrapping the entire function body in
/// an unsafe block once.
suggest_unsafe_block: bool,
/// Track whether we're currently inside a `&raw const/mut` expression.
/// Used to allow safe access to union fields when only taking their address.
in_raw_borrow: bool,
}

impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
Expand Down Expand Up @@ -218,6 +221,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
inside_adt: false,
warnings: self.warnings,
suggest_unsafe_block: self.suggest_unsafe_block,
in_raw_borrow: false,
};
// params in THIR may be unsafe, e.g. a union pattern.
for param in &inner_thir.params {
Expand Down Expand Up @@ -538,14 +542,22 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
}
}
ExprKind::RawBorrow { arg, .. } => {
// Set flag when entering raw borrow context
let old_in_raw_borrow = self.in_raw_borrow;
self.in_raw_borrow = is_direct_place_expr(self.thir, arg);

if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
&& let ExprKind::Deref { arg } = self.thir[arg].kind
{
// Taking a raw ref to a deref place expr is always safe.
// Make sure the expression we're deref'ing is safe, though.
visit::walk_expr(self, &self.thir[arg]);
return;
}

visit::walk_expr(self, &self.thir[arg]);
// Restore previous state
self.in_raw_borrow = old_in_raw_borrow;
return;
}
ExprKind::Deref { arg } => {
if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
Expand Down Expand Up @@ -651,7 +663,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
"union fields that need dropping should be impossible: {assigned_ty}"
);
}
} else {
} else if !self.in_raw_borrow {
// Union field access is unsafe because the field may be uninitialized.
// However, `&raw const/mut union.field` is safe since it only computes
// the field's address without reading the potentially uninitialized value.
self.requires_unsafe(expr.span, AccessToUnionField);
}
}
Expand Down Expand Up @@ -705,6 +720,19 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
}
}

fn is_direct_place_expr<'a, 'tcx>(thir: &'a Thir<'tcx>, expr_id: ExprId) -> bool {
match thir[expr_id].kind {
// Direct place expressions that don't read values
ExprKind::Field { .. } | ExprKind::VarRef { .. } | ExprKind::UpvarRef { .. } => true,

// Scope is transparent for place expressions
ExprKind::Scope { value, .. } => is_direct_place_expr(thir, value),

// Any other expression (including Deref, method calls, etc.) reads values
_ => false,
}
}

#[derive(Clone)]
enum SafetyContext {
Safe,
Expand Down Expand Up @@ -1187,6 +1215,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
inside_adt: false,
warnings: &mut warnings,
suggest_unsafe_block: true,
in_raw_borrow: false,
};
// params in THIR may be unsafe, e.g. a union pattern.
for param in &thir.params {
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/union/union-unsafe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ union U4<T: Copy> {
a: T,
}

union U5 {
a: usize,
}

union URef {
p: &'static mut i32,
}
Expand All @@ -31,6 +35,12 @@ fn deref_union_field(mut u: URef) {
*(u.p) = 13; //~ ERROR access to union field is unsafe
}

fn raw_deref_union_field(mut u: URef) {
let _p = &raw const *(u.p);
//~^ ERROR access to union field is unsafe
//~| ERROR access to union field is unsafe
}

fn assign_noncopy_union_field(mut u: URefCell) {
u.a = (ManuallyDrop::new(RefCell::new(0)), 1); // OK (assignment does not drop)
u.a.0 = ManuallyDrop::new(RefCell::new(0)); // OK (assignment does not drop)
Expand All @@ -57,6 +67,19 @@ fn main() {
let a = u1.a; //~ ERROR access to union field is unsafe
u1.a = 11; // OK

let mut u2 = U1 { a: 10 };
let a = &raw mut u2.a; // OK
unsafe { *a = 3 };

let mut u3 = U1 { a: 10 };
let a = std::ptr::addr_of_mut!(u3.a); // OK
unsafe { *a = 14 };

let u4 = U5 { a: 2 };
let vec = vec![1, 2, 3];
let _a = &raw const vec[u4.a]; //~ ERROR access to union field is unsafe
//~^ ERROR access to union field is unsafe
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something is wrong here, the error should not be emitted twice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this should be WAY better now, also added more comments for tests and code and also removed this global state


let U1 { a } = u1; //~ ERROR access to union field is unsafe
if let U1 { a: 12 } = u1 {} //~ ERROR access to union field is unsafe
if let Some(U1 { a: 13 }) = Some(u1) {} //~ ERROR access to union field is unsafe
Expand Down
56 changes: 45 additions & 11 deletions tests/ui/union/union-unsafe.stderr
Original file line number Diff line number Diff line change
@@ -1,83 +1,117 @@
error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:31:6
--> $DIR/union-unsafe.rs:35:6
|
LL | *(u.p) = 13;
| ^^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior

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

error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:39:26
|
LL | let _p = &raw const *(u.p);
| ^^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:53:6
|
LL | *u3.a = T::default();
| ^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior

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

error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:57:13
--> $DIR/union-unsafe.rs:67:13
|
LL | let a = u1.a;
| ^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior

error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:60:14
--> $DIR/union-unsafe.rs:80:29
|
LL | let _a = &raw const vec[u4.a];
| ^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior

error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:80:29
|
LL | let _a = &raw const vec[u4.a];
| ^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:83:14
|
LL | let U1 { a } = u1;
| ^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior

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

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

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

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

error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:75:6
--> $DIR/union-unsafe.rs:98:6
|
LL | *u3.a = String::from("new");
| ^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior

error: aborting due to 10 previous errors
error: aborting due to 14 previous errors

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