Skip to content

Commit 2f278c5

Browse files
committed
Auto merge of #42083 - petrochenkov:safeassign, r=nikomatsakis
Make assignments to `Copy` union fields safe This is an accompanying PR to PR #42068 stabilizing FFI unions. This was first proposed in #32836 (comment), see subsequent comments as well. Assignments to `Copy` union fields do not read any data from the union and are [equivalent](#32836 (comment)) to whole union assignments, which are safe, so they should be safe as well. This removes a significant number of "false positive" unsafe blocks, in code dealing with FFI unions in particular. It desirable to make this change now, together with stabilization of FFI unions, because now it affecfts only unstable code, but later it will cause warnings/errors caused by `unused_unsafe` lint in stable code. cc #32836 r? @nikomatsakis
2 parents 2db17c8 + fa13cd3 commit 2f278c5

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

src/librustc/middle/effect.rs

+24
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ fn type_is_unsafe_function(ty: Ty) -> bool {
5252
struct EffectCheckVisitor<'a, 'tcx: 'a> {
5353
tcx: TyCtxt<'a, 'tcx, 'tcx>,
5454
tables: &'a ty::TypeckTables<'tcx>,
55+
body_id: hir::BodyId,
5556

5657
/// Whether we're in an unsafe context.
5758
unsafe_context: UnsafeContext,
@@ -99,10 +100,13 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> {
99100

100101
fn visit_nested_body(&mut self, body: hir::BodyId) {
101102
let old_tables = self.tables;
103+
let old_body_id = self.body_id;
102104
self.tables = self.tcx.body_tables(body);
105+
self.body_id = body;
103106
let body = self.tcx.hir.body(body);
104107
self.visit_body(body);
105108
self.tables = old_tables;
109+
self.body_id = old_body_id;
106110
}
107111

108112
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, fn_decl: &'tcx hir::FnDecl,
@@ -218,6 +222,25 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> {
218222
}
219223
}
220224
}
225+
hir::ExprAssign(ref lhs, ref rhs) => {
226+
if let hir::ExprField(ref base_expr, field) = lhs.node {
227+
if let ty::TyAdt(adt, ..) = self.tables.expr_ty_adjusted(base_expr).sty {
228+
if adt.is_union() {
229+
let field_ty = self.tables.expr_ty_adjusted(lhs);
230+
let owner_def_id = self.tcx.hir.body_owner_def_id(self.body_id);
231+
let param_env = self.tcx.param_env(owner_def_id);
232+
if field_ty.moves_by_default(self.tcx, param_env, field.span) {
233+
self.require_unsafe(field.span,
234+
"assignment to non-`Copy` union field");
235+
}
236+
// Do not walk the field expr again.
237+
intravisit::walk_expr(self, base_expr);
238+
intravisit::walk_expr(self, rhs);
239+
return
240+
}
241+
}
242+
}
243+
}
221244
_ => {}
222245
}
223246

@@ -243,6 +266,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
243266
let mut visitor = EffectCheckVisitor {
244267
tcx: tcx,
245268
tables: &ty::TypeckTables::empty(),
269+
body_id: hir::BodyId { node_id: ast::CRATE_NODE_ID },
246270
unsafe_context: UnsafeContext::new(SafeContext),
247271
};
248272

src/test/compile-fail/union/union-unsafe.rs

+38-7
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,46 @@
1010

1111
#![feature(untagged_unions)]
1212

13-
union U {
13+
union U1 {
1414
a: u8
1515
}
1616

17+
union U2 {
18+
a: String
19+
}
20+
21+
union U3<T> {
22+
a: T
23+
}
24+
25+
union U4<T: Copy> {
26+
a: T
27+
}
28+
29+
fn generic_noncopy<T: Default>() {
30+
let mut u3 = U3 { a: T::default() };
31+
u3.a = T::default(); //~ ERROR assignment to non-`Copy` union field requires unsafe
32+
}
33+
34+
fn generic_copy<T: Copy + Default>() {
35+
let mut u3 = U3 { a: T::default() };
36+
u3.a = T::default(); // OK
37+
let mut u4 = U4 { a: T::default() };
38+
u4.a = T::default(); // OK
39+
}
40+
1741
fn main() {
18-
let mut u = U { a: 10 }; // OK
19-
let a = u.a; //~ ERROR access to union field requires unsafe function or block
20-
u.a = 11; //~ ERROR access to union field requires unsafe function or block
21-
let U { a } = u; //~ ERROR matching on union field requires unsafe function or block
22-
if let U { a: 12 } = u {} //~ ERROR matching on union field requires unsafe function or block
23-
// let U { .. } = u; // OK
42+
let mut u1 = U1 { a: 10 }; // OK
43+
let a = u1.a; //~ ERROR access to union field requires unsafe
44+
u1.a = 11; // OK
45+
let U1 { a } = u1; //~ ERROR matching on union field requires unsafe
46+
if let U1 { a: 12 } = u1 {} //~ ERROR matching on union field requires unsafe
47+
// let U1 { .. } = u1; // OK
48+
49+
let mut u2 = U2 { a: String::from("old") }; // OK
50+
u2.a = String::from("new"); //~ ERROR assignment to non-`Copy` union field requires unsafe
51+
let mut u3 = U3 { a: 0 }; // OK
52+
u3.a = 1; // OK
53+
let mut u3 = U3 { a: String::from("old") }; // OK
54+
u3.a = String::from("new"); //~ ERROR assignment to non-`Copy` union field requires unsafe
2455
}

0 commit comments

Comments
 (0)