Skip to content

Commit bea0b15

Browse files
committed
Implement drop translation and add lint for unions with drop fields
Fix some typeck bugs blocking drop tests
1 parent e88d4ca commit bea0b15

File tree

7 files changed

+134
-14
lines changed

7 files changed

+134
-14
lines changed

src/librustc_lint/builtin.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,3 +1164,36 @@ impl LateLintPass for UnstableFeatures {
11641164
}
11651165
}
11661166
}
1167+
1168+
/// Lint for unions that contain fields with possibly non-trivial destructors.
1169+
pub struct UnionsWithDropFields;
1170+
1171+
declare_lint! {
1172+
UNIONS_WITH_DROP_FIELDS,
1173+
Warn,
1174+
"use of unions that contain fields with possibly non-trivial drop code"
1175+
}
1176+
1177+
impl LintPass for UnionsWithDropFields {
1178+
fn get_lints(&self) -> LintArray {
1179+
lint_array!(UNIONS_WITH_DROP_FIELDS)
1180+
}
1181+
}
1182+
1183+
impl LateLintPass for UnionsWithDropFields {
1184+
fn check_item(&mut self, ctx: &LateContext, item: &hir::Item) {
1185+
if let hir::ItemUnion(ref vdata, _) = item.node {
1186+
let param_env = &ty::ParameterEnvironment::for_item(ctx.tcx, item.id);
1187+
for field in vdata.fields() {
1188+
let field_ty = ctx.tcx.node_id_to_type(field.id);
1189+
if ctx.tcx.type_needs_drop_given_env(field_ty, param_env) {
1190+
ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
1191+
field.span,
1192+
"union contains a field with possibly non-trivial drop code, \
1193+
drop code of union fields is ignored when dropping the union");
1194+
return;
1195+
}
1196+
}
1197+
}
1198+
}
1199+
}

src/librustc_lint/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
128128
InvalidNoMangleItems,
129129
PluginAsLibrary,
130130
MutableTransmutes,
131+
UnionsWithDropFields,
131132
);
132133

133134
add_builtin_with_new!(sess,

src/librustc_trans/glue.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
267267

268268
fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
269269
t: Ty<'tcx>,
270-
v0: ValueRef)
270+
v0: ValueRef,
271+
shallow_drop: bool)
271272
-> Block<'blk, 'tcx>
272273
{
273274
debug!("trans_struct_drop t: {}", t);
@@ -286,7 +287,9 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
286287

287288
// Issue #23611: schedule cleanup of contents, re-inspecting the
288289
// discriminant (if any) in case of variant swap in drop code.
289-
bcx.fcx.schedule_drop_adt_contents(contents_scope, v0, t);
290+
if !shallow_drop {
291+
bcx.fcx.schedule_drop_adt_contents(contents_scope, v0, t);
292+
}
290293

291294
let (sized_args, unsized_args);
292295
let args: &[ValueRef] = if type_is_sized(tcx, t) {
@@ -470,9 +473,6 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
470473
trans_exchange_free_ty(bcx, llbox, content_ty, DebugLoc::None)
471474
}
472475
}
473-
ty::TyUnion(..) => {
474-
unimplemented_unions!();
475-
}
476476
ty::TyTrait(..) => {
477477
// No support in vtable for distinguishing destroying with
478478
// versus without calling Drop::drop. Assert caller is
@@ -491,6 +491,13 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
491491
if def.dtor_kind().is_present() && !skip_dtor => {
492492
trans_struct_drop(bcx, t, v0)
493493
}
494+
ty::TyUnion(def, _) => {
495+
if def.dtor_kind().is_present() && !skip_dtor {
496+
trans_struct_drop(bcx, t, v0, true)
497+
} else {
498+
bcx
499+
}
500+
}
494501
_ => {
495502
if bcx.fcx.type_needs_drop(t) {
496503
drop_structural_ty(bcx, v0, t)

src/librustc_typeck/check/mod.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,11 +3235,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
32353235
Some((type_did, self.tcx.expect_variant_def(def)))
32363236
}
32373237
Def::TyAlias(did) => {
3238-
if let Some(&ty::TyStruct(adt, _)) = self.tcx.opt_lookup_item_type(did)
3239-
.map(|scheme| &scheme.ty.sty) {
3240-
Some((did, adt.struct_variant()))
3241-
} else {
3242-
None
3238+
match self.tcx.opt_lookup_item_type(did).map(|scheme| &scheme.ty.sty) {
3239+
Some(&ty::TyStruct(adt, _)) |
3240+
Some(&ty::TyUnion(adt, _)) => Some((did, adt.struct_variant())),
3241+
_ => None,
32433242
}
32443243
}
32453244
_ => None

src/librustc_typeck/collect.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1450,7 +1450,8 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
14501450

14511451
ItemTy(_, ref generics) |
14521452
ItemEnum(_, ref generics) |
1453-
ItemStruct(_, ref generics) => {
1453+
ItemStruct(_, ref generics) |
1454+
ItemUnion(_, ref generics) => {
14541455
allow_defaults = true;
14551456
generics
14561457
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(untagged_unions)]
12+
#![allow(dead_code)]
13+
#![deny(unions_with_drop_fields)]
14+
15+
union U {
16+
a: u8, // OK
17+
}
18+
19+
union W {
20+
a: String, //~ ERROR union contains a field with possibly non-trivial drop code
21+
b: String, // OK, only one field is reported
22+
}
23+
24+
struct S(String);
25+
26+
// `S` doesn't implement `Drop` trait, but still has non-trivial destructor
27+
union Y {
28+
a: S, //~ ERROR union contains a field with possibly non-trivial drop code
29+
}
30+
31+
// We don't know if `T` is trivially-destructable or not until trans
32+
union J<T> {
33+
a: T, //~ ERROR union contains a field with possibly non-trivial drop code
34+
}
35+
36+
union H<T: Copy> {
37+
a: T, // OK, `T` is `Copy`, no destructor
38+
}
39+
40+
fn main() {}

src/test/run-pass/union-drop.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,54 @@
1212

1313
#![feature(untagged_unions)]
1414

15+
struct S;
16+
1517
union U {
1618
a: u8
1719
}
1820

21+
union W {
22+
a: S,
23+
}
24+
25+
union Y {
26+
a: S,
27+
}
28+
29+
impl Drop for S {
30+
fn drop(&mut self) {
31+
unsafe { CHECK += 10; }
32+
}
33+
}
34+
1935
impl Drop for U {
20-
fn drop(&mut self) {}
36+
fn drop(&mut self) {
37+
unsafe { CHECK += 1; }
38+
}
2139
}
2240

41+
impl Drop for W {
42+
fn drop(&mut self) {
43+
unsafe { CHECK += 1; }
44+
}
45+
}
46+
47+
static mut CHECK: u8 = 0;
48+
2349
fn main() {
24-
// 'unions are not fully implemented', src/librustc_trans/glue.rs:567
25-
// let u = U { a: 1 };
50+
unsafe {
51+
assert_eq!(CHECK, 0);
52+
{
53+
let u = U { a: 1 };
54+
}
55+
assert_eq!(CHECK, 1); // 1, dtor of U is called
56+
{
57+
let w = W { a: S };
58+
}
59+
assert_eq!(CHECK, 2); // 2, not 11, dtor of S is not called
60+
{
61+
let y = Y { a: S };
62+
}
63+
assert_eq!(CHECK, 2); // 2, not 12, dtor of S is not called
64+
}
2665
}

0 commit comments

Comments
 (0)