1
Fork 0

Implement AssignToDroppingUnionField in THIR unsafeck

This commit is contained in:
LeSeulArtichaut 2021-07-23 15:26:12 +02:00
parent e742158ef5
commit c5dda05e4e
3 changed files with 50 additions and 20 deletions

View file

@ -5,7 +5,7 @@ use rustc_errors::struct_span_err;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::mir::BorrowKind; use rustc_middle::mir::BorrowKind;
use rustc_middle::thir::*; use rustc_middle::thir::*;
use rustc_middle::ty::{self, ParamEnv, TyCtxt}; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level; use rustc_session::lint::Level;
use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::def_id::{DefId, LocalDefId};
@ -27,7 +27,9 @@ struct UnsafetyVisitor<'a, 'tcx> {
/// The `#[target_feature]` attributes of the body. Used for checking /// The `#[target_feature]` attributes of the body. Used for checking
/// calls to functions with `#[target_feature]` (RFC 2396). /// calls to functions with `#[target_feature]` (RFC 2396).
body_target_features: &'tcx Vec<Symbol>, body_target_features: &'tcx Vec<Symbol>,
in_possible_lhs_union_assign: bool, /// When inside the LHS of an assignment to a field, this is the type
/// of the LHS and the span of the assignment expression.
assignment_info: Option<(Ty<'tcx>, Span)>,
in_union_destructure: bool, in_union_destructure: bool,
param_env: ParamEnv<'tcx>, param_env: ParamEnv<'tcx>,
inside_adt: bool, inside_adt: bool,
@ -287,7 +289,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
} }
fn visit_expr(&mut self, expr: &Expr<'tcx>) { fn visit_expr(&mut self, expr: &Expr<'tcx>) {
// could we be in a the LHS of an assignment of a union? // could we be in the LHS of an assignment to a field?
match expr.kind { match expr.kind {
ExprKind::Field { .. } ExprKind::Field { .. }
| ExprKind::VarRef { .. } | ExprKind::VarRef { .. }
@ -329,7 +331,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::InlineAsm { .. } | ExprKind::InlineAsm { .. }
| ExprKind::LlvmInlineAsm { .. } | ExprKind::LlvmInlineAsm { .. }
| ExprKind::LogicalOp { .. } | ExprKind::LogicalOp { .. }
| ExprKind::Use { .. } => self.in_possible_lhs_union_assign = false, | ExprKind::Use { .. } => {
// We don't need to save the old value and restore it
// because all the place expressions can't have more
// than one child.
self.assignment_info = None;
}
}; };
match expr.kind { match expr.kind {
ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => { ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
@ -409,11 +416,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
self.safety_context = closure_visitor.safety_context; self.safety_context = closure_visitor.safety_context;
} }
ExprKind::Field { lhs, .. } => { ExprKind::Field { lhs, .. } => {
// assigning to union field is okay for AccessToUnionField let lhs = &self.thir[lhs];
if let ty::Adt(adt_def, _) = &self.thir[lhs].ty.kind() { if let ty::Adt(adt_def, _) = lhs.ty.kind() {
if adt_def.is_union() { if adt_def.is_union() {
if self.in_possible_lhs_union_assign { if let Some((assigned_ty, assignment_span)) = self.assignment_info {
// FIXME: trigger AssignToDroppingUnionField unsafety if needed // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
if !(assigned_ty
.ty_adt_def()
.map_or(false, |adt| adt.is_manually_drop())
|| assigned_ty
.is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env))
{
self.requires_unsafe(assignment_span, AssignToDroppingUnionField);
} else {
// write to non-drop union field, safe
}
} else { } else {
self.requires_unsafe(expr.span, AccessToUnionField); self.requires_unsafe(expr.span, AccessToUnionField);
} }
@ -421,9 +438,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
} }
} }
ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => { ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
let lhs = &self.thir[lhs];
// First, check whether we are mutating a layout constrained field // First, check whether we are mutating a layout constrained field
let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx); let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
visit::walk_expr(&mut visitor, &self.thir[lhs]); visit::walk_expr(&mut visitor, lhs);
if visitor.found { if visitor.found {
self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField); self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
} }
@ -431,10 +449,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
// Second, check for accesses to union fields // Second, check for accesses to union fields
// don't have any special handling for AssignOp since it causes a read *and* write to lhs // don't have any special handling for AssignOp since it causes a read *and* write to lhs
if matches!(expr.kind, ExprKind::Assign { .. }) { if matches!(expr.kind, ExprKind::Assign { .. }) {
// assigning to a union is safe, check here so it doesn't get treated as a read later self.assignment_info = Some((lhs.ty, expr.span));
self.in_possible_lhs_union_assign = true; visit::walk_expr(self, lhs);
visit::walk_expr(self, &self.thir()[lhs]); self.assignment_info = None;
self.in_possible_lhs_union_assign = false;
visit::walk_expr(self, &self.thir()[rhs]); visit::walk_expr(self, &self.thir()[rhs]);
return; // we have already visited everything by now return; // we have already visited everything by now
} }
@ -506,12 +523,9 @@ enum UnsafeOpKind {
UseOfMutableStatic, UseOfMutableStatic,
UseOfExternStatic, UseOfExternStatic,
DerefOfRawPointer, DerefOfRawPointer,
#[allow(dead_code)] // FIXME
AssignToDroppingUnionField, AssignToDroppingUnionField,
AccessToUnionField, AccessToUnionField,
#[allow(dead_code)] // FIXME
MutationOfLayoutConstrainedField, MutationOfLayoutConstrainedField,
#[allow(dead_code)] // FIXME
BorrowOfLayoutConstrainedField, BorrowOfLayoutConstrainedField,
CallToFunctionWith, CallToFunctionWith,
} }
@ -619,7 +633,7 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
hir_context: hir_id, hir_context: hir_id,
body_unsafety, body_unsafety,
body_target_features, body_target_features,
in_possible_lhs_union_assign: false, assignment_info: None,
in_union_destructure: false, in_union_destructure: false,
param_env: tcx.param_env(def.did), param_env: tcx.param_env(def.did),
inside_adt: false, inside_adt: false,

View file

@ -36,8 +36,8 @@ fn deref_union_field(mut u: URef) {
fn assign_noncopy_union_field(mut u: URefCell) { fn assign_noncopy_union_field(mut u: URefCell) {
// FIXME(thir-unsafeck) // FIXME(thir-unsafeck)
u.a = (RefCell::new(0), 1); //[mir]~ ERROR assignment to union field that might need dropping u.a = (RefCell::new(0), 1); //~ ERROR assignment to union field that might need dropping
u.a.0 = RefCell::new(0); //[mir]~ ERROR assignment to union field that might need dropping u.a.0 = RefCell::new(0); //~ ERROR assignment to union field that might need dropping
u.a.1 = 1; // OK u.a.1 = 1; // OK
} }

View file

@ -6,6 +6,22 @@ LL | *(u.p) = 13;
| |
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:39:5
|
LL | u.a = (RefCell::new(0), 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
|
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:40:5
|
LL | u.a.0 = RefCell::new(0);
| ^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
|
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
error[E0133]: access to union field is unsafe and requires unsafe function or block error[E0133]: access to union field is unsafe and requires unsafe function or block
--> $DIR/union-unsafe.rs:47:6 --> $DIR/union-unsafe.rs:47:6
| |
@ -70,6 +86,6 @@ LL | *u3.a = String::from("new");
| |
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
error: aborting due to 9 previous errors error: aborting due to 11 previous errors
For more information about this error, try `rustc --explain E0133`. For more information about this error, try `rustc --explain E0133`.