Skip to content

Commit fa1bbfb

Browse files
Rollup merge of rust-lang#87403 - LeSeulArtichaut:assign-dropping-union, r=oli-obk
Implement `AssignToDroppingUnionField` in THIR unsafeck r? `@oli-obk` cc rust-lang/project-thir-unsafeck#7
2 parents aac9783 + c5dda05 commit fa1bbfb

File tree

3 files changed

+50
-20
lines changed

3 files changed

+50
-20
lines changed

compiler/rustc_mir_build/src/check_unsafety.rs

+31-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_errors::struct_span_err;
55
use rustc_hir as hir;
66
use rustc_middle::mir::BorrowKind;
77
use rustc_middle::thir::*;
8-
use rustc_middle::ty::{self, ParamEnv, TyCtxt};
8+
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
99
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
1010
use rustc_session::lint::Level;
1111
use rustc_span::def_id::{DefId, LocalDefId};
@@ -27,7 +27,9 @@ struct UnsafetyVisitor<'a, 'tcx> {
2727
/// The `#[target_feature]` attributes of the body. Used for checking
2828
/// calls to functions with `#[target_feature]` (RFC 2396).
2929
body_target_features: &'tcx Vec<Symbol>,
30-
in_possible_lhs_union_assign: bool,
30+
/// When inside the LHS of an assignment to a field, this is the type
31+
/// of the LHS and the span of the assignment expression.
32+
assignment_info: Option<(Ty<'tcx>, Span)>,
3133
in_union_destructure: bool,
3234
param_env: ParamEnv<'tcx>,
3335
inside_adt: bool,
@@ -287,7 +289,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
287289
}
288290

289291
fn visit_expr(&mut self, expr: &Expr<'tcx>) {
290-
// could we be in a the LHS of an assignment of a union?
292+
// could we be in the LHS of an assignment to a field?
291293
match expr.kind {
292294
ExprKind::Field { .. }
293295
| ExprKind::VarRef { .. }
@@ -329,7 +331,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
329331
| ExprKind::InlineAsm { .. }
330332
| ExprKind::LlvmInlineAsm { .. }
331333
| ExprKind::LogicalOp { .. }
332-
| ExprKind::Use { .. } => self.in_possible_lhs_union_assign = false,
334+
| ExprKind::Use { .. } => {
335+
// We don't need to save the old value and restore it
336+
// because all the place expressions can't have more
337+
// than one child.
338+
self.assignment_info = None;
339+
}
333340
};
334341
match expr.kind {
335342
ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
@@ -409,32 +416,42 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
409416
self.safety_context = closure_visitor.safety_context;
410417
}
411418
ExprKind::Field { lhs, .. } => {
412-
// assigning to union field is okay for AccessToUnionField
413-
if let ty::Adt(adt_def, _) = &self.thir[lhs].ty.kind() {
419+
let lhs = &self.thir[lhs];
420+
if let ty::Adt(adt_def, _) = lhs.ty.kind() {
414421
if adt_def.is_union() {
415-
if self.in_possible_lhs_union_assign {
416-
// FIXME: trigger AssignToDroppingUnionField unsafety if needed
422+
if let Some((assigned_ty, assignment_span)) = self.assignment_info {
423+
// To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
424+
if !(assigned_ty
425+
.ty_adt_def()
426+
.map_or(false, |adt| adt.is_manually_drop())
427+
|| assigned_ty
428+
.is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env))
429+
{
430+
self.requires_unsafe(assignment_span, AssignToDroppingUnionField);
431+
} else {
432+
// write to non-drop union field, safe
433+
}
417434
} else {
418435
self.requires_unsafe(expr.span, AccessToUnionField);
419436
}
420437
}
421438
}
422439
}
423440
ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
441+
let lhs = &self.thir[lhs];
424442
// First, check whether we are mutating a layout constrained field
425443
let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
426-
visit::walk_expr(&mut visitor, &self.thir[lhs]);
444+
visit::walk_expr(&mut visitor, lhs);
427445
if visitor.found {
428446
self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
429447
}
430448

431449
// Second, check for accesses to union fields
432450
// don't have any special handling for AssignOp since it causes a read *and* write to lhs
433451
if matches!(expr.kind, ExprKind::Assign { .. }) {
434-
// assigning to a union is safe, check here so it doesn't get treated as a read later
435-
self.in_possible_lhs_union_assign = true;
436-
visit::walk_expr(self, &self.thir()[lhs]);
437-
self.in_possible_lhs_union_assign = false;
452+
self.assignment_info = Some((lhs.ty, expr.span));
453+
visit::walk_expr(self, lhs);
454+
self.assignment_info = None;
438455
visit::walk_expr(self, &self.thir()[rhs]);
439456
return; // we have already visited everything by now
440457
}
@@ -506,12 +523,9 @@ enum UnsafeOpKind {
506523
UseOfMutableStatic,
507524
UseOfExternStatic,
508525
DerefOfRawPointer,
509-
#[allow(dead_code)] // FIXME
510526
AssignToDroppingUnionField,
511527
AccessToUnionField,
512-
#[allow(dead_code)] // FIXME
513528
MutationOfLayoutConstrainedField,
514-
#[allow(dead_code)] // FIXME
515529
BorrowOfLayoutConstrainedField,
516530
CallToFunctionWith,
517531
}
@@ -619,7 +633,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
619633
hir_context: hir_id,
620634
body_unsafety,
621635
body_target_features,
622-
in_possible_lhs_union_assign: false,
636+
assignment_info: None,
623637
in_union_destructure: false,
624638
param_env: tcx.param_env(def.did),
625639
inside_adt: false,

src/test/ui/union/union-unsafe.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ fn deref_union_field(mut u: URef) {
3636

3737
fn assign_noncopy_union_field(mut u: URefCell) {
3838
// FIXME(thir-unsafeck)
39-
u.a = (RefCell::new(0), 1); //[mir]~ ERROR assignment to union field that might need dropping
40-
u.a.0 = RefCell::new(0); //[mir]~ ERROR assignment to union field that might need dropping
39+
u.a = (RefCell::new(0), 1); //~ ERROR assignment to union field that might need dropping
40+
u.a.0 = RefCell::new(0); //~ ERROR assignment to union field that might need dropping
4141
u.a.1 = 1; // OK
4242
}
4343

src/test/ui/union/union-unsafe.thir.stderr

+17-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ LL | *(u.p) = 13;
66
|
77
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
88

9+
error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
10+
--> $DIR/union-unsafe.rs:39:5
11+
|
12+
LL | u.a = (RefCell::new(0), 1);
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
14+
|
15+
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
16+
17+
error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
18+
--> $DIR/union-unsafe.rs:40:5
19+
|
20+
LL | u.a.0 = RefCell::new(0);
21+
| ^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
22+
|
23+
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
24+
925
error[E0133]: access to union field is unsafe and requires unsafe function or block
1026
--> $DIR/union-unsafe.rs:47:6
1127
|
@@ -70,6 +86,6 @@ LL | *u3.a = String::from("new");
7086
|
7187
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
7288

73-
error: aborting due to 9 previous errors
89+
error: aborting due to 11 previous errors
7490

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

0 commit comments

Comments
 (0)