Skip to content

Commit 6a97fba

Browse files
committed
Rollup merge of rust-lang#22818 - pnkfelix:fsk-issue-22536, r=nikomatsakis
Ensure we do not zero when \"moving\" types that are Copy. Uses more precise `type_needs_drop` for deciding about emitting cleanup code. Added notes about the weaknesses regarding `ty::type_contents` here. Fix rust-lang#22536
2 parents 5d4e017 + 9a6e3b9 commit 6a97fba

File tree

13 files changed

+114
-18
lines changed

13 files changed

+114
-18
lines changed

src/libcore/intrinsics.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,12 @@ extern "rust-intrinsic" {
241241
/// will trigger a compiler error.
242242
pub fn return_address() -> *const u8;
243243

244-
/// Returns `true` if a type requires drop glue.
244+
/// Returns `true` if the actual type given as `T` requires drop
245+
/// glue; returns `false` if the actual type provided for `T`
246+
/// implements `Copy`.
247+
///
248+
/// If the actual type neither requires drop glue nor implements
249+
/// `Copy`, then may return `true` or `false`.
245250
pub fn needs_drop<T>() -> bool;
246251

247252
/// Returns `true` if a type is managed (will be allocated on the local heap)

src/librustc_trans/trans/_match.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,7 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
14991499
fn create_dummy_locals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
15001500
pat: &ast::Pat)
15011501
-> Block<'blk, 'tcx> {
1502+
let _icx = push_ctxt("create_dummy_locals");
15021503
// create dummy memory for the variables if we have no
15031504
// value to store into them immediately
15041505
let tcx = bcx.tcx();

src/librustc_trans/trans/callee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
734734
};
735735
if !is_rust_fn ||
736736
type_of::return_uses_outptr(ccx, ret_ty) ||
737-
common::type_needs_drop(bcx.tcx(), ret_ty) {
737+
bcx.fcx.type_needs_drop(ret_ty) {
738738
// Push the out-pointer if we use an out-pointer for this
739739
// return type, otherwise push "undef".
740740
if common::type_is_zero_size(ccx, ret_ty) {

src/librustc_trans/trans/cleanup.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
386386
cleanup_scope: ScopeId,
387387
val: ValueRef,
388388
ty: Ty<'tcx>) {
389-
if !common::type_needs_drop(self.ccx.tcx(), ty) { return; }
389+
if !self.type_needs_drop(ty) { return; }
390390
let drop = box DropValue {
391391
is_immediate: false,
392392
must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
@@ -408,7 +408,8 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
408408
cleanup_scope: ScopeId,
409409
val: ValueRef,
410410
ty: Ty<'tcx>) {
411-
if !common::type_needs_drop(self.ccx.tcx(), ty) { return; }
411+
if !self.type_needs_drop(ty) { return; }
412+
412413
let drop = box DropValue {
413414
is_immediate: false,
414415
must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
@@ -432,7 +433,7 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
432433
val: ValueRef,
433434
ty: Ty<'tcx>) {
434435

435-
if !common::type_needs_drop(self.ccx.tcx(), ty) { return; }
436+
if !self.type_needs_drop(ty) { return; }
436437
let drop = box DropValue {
437438
is_immediate: true,
438439
must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
@@ -1007,6 +1008,7 @@ impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> {
10071008
bcx: Block<'blk, 'tcx>,
10081009
debug_loc: DebugLoc)
10091010
-> Block<'blk, 'tcx> {
1011+
let _icx = base::push_ctxt("<DropValue as Cleanup>::trans");
10101012
let bcx = if self.is_immediate {
10111013
glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc)
10121014
} else {

src/librustc_trans/trans/common.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,43 @@ pub fn type_needs_unwind_cleanup<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<
213213
}
214214
}
215215

216+
/// If `type_needs_drop` returns true, then `ty` is definitely
217+
/// non-copy and *might* have a destructor attached; if it returns
218+
/// false, then `ty` definitely has no destructor (i.e. no drop glue).
219+
///
220+
/// (Note that this implies that if `ty` has a destructor attached,
221+
/// then `type_needs_drop` will definitely return `true` for `ty`.)
216222
pub fn type_needs_drop<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool {
217-
ty::type_contents(cx, ty).needs_drop(cx)
223+
type_needs_drop_given_env(cx, ty, &ty::empty_parameter_environment(cx))
224+
}
225+
226+
/// Core implementation of type_needs_drop, potentially making use of
227+
/// and/or updating caches held in the `param_env`.
228+
fn type_needs_drop_given_env<'a,'tcx>(cx: &ty::ctxt<'tcx>,
229+
ty: Ty<'tcx>,
230+
param_env: &ty::ParameterEnvironment<'a,'tcx>) -> bool {
231+
// Issue #22536: We first query type_moves_by_default. It sees a
232+
// normalized version of the type, and therefore will definitely
233+
// know whether the type implements Copy (and thus needs no
234+
// cleanup/drop/zeroing) ...
235+
let implements_copy = !ty::type_moves_by_default(&param_env, DUMMY_SP, ty);
236+
237+
if implements_copy { return false; }
238+
239+
// ... (issue #22536 continued) but as an optimization, still use
240+
// prior logic of asking if the `needs_drop` bit is set; we need
241+
// not zero non-Copy types if they have no destructor.
242+
243+
// FIXME(#22815): Note that calling `ty::type_contents` is a
244+
// conservative heuristic; it may report that `needs_drop` is set
245+
// when actual type does not actually have a destructor associated
246+
// with it. But since `ty` absolutely did not have the `Copy`
247+
// bound attached (see above), it is sound to treat it as having a
248+
// destructor (e.g. zero its memory on move).
249+
250+
let contents = ty::type_contents(cx, ty);
251+
debug!("type_needs_drop ty={} contents={:?}", ty.repr(cx), contents);
252+
contents.needs_drop(cx)
218253
}
219254

220255
fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
@@ -534,6 +569,12 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
534569
self.param_substs,
535570
value)
536571
}
572+
573+
/// This is the same as `common::type_needs_drop`, except that it
574+
/// may use or update caches within this `FunctionContext`.
575+
pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool {
576+
type_needs_drop_given_env(self.ccx.tcx(), ty, &self.param_env)
577+
}
537578
}
538579

539580
// Basic block context. We create a block context for each basic block

src/librustc_trans/trans/controlflow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr)
7777
-> Block<'blk, 'tcx> {
7878
let _icx = push_ctxt("trans_stmt_semi");
7979
let ty = expr_ty(cx, e);
80-
if type_needs_drop(cx.tcx(), ty) {
80+
if cx.fcx.type_needs_drop(ty) {
8181
expr::trans_to_lvalue(cx, e, "stmt").bcx
8282
} else {
8383
expr::trans_into(cx, e, expr::Ignore)

src/librustc_trans/trans/datum.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@ impl KindOps for Lvalue {
311311
val: ValueRef,
312312
ty: Ty<'tcx>)
313313
-> Block<'blk, 'tcx> {
314-
if type_needs_drop(bcx.tcx(), ty) {
314+
let _icx = push_ctxt("<Lvalue as KindOps>::post_store");
315+
if bcx.fcx.type_needs_drop(ty) {
315316
// cancel cleanup of affine values by zeroing out
316317
let () = zero_mem(bcx, val, ty);
317318
bcx
@@ -656,7 +657,7 @@ impl<'tcx, K: KindOps + fmt::Debug> Datum<'tcx, K> {
656657
/// scalar-ish (like an int or a pointer) which (1) does not require drop glue and (2) is
657658
/// naturally passed around by value, and not by reference.
658659
pub fn to_llscalarish<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef {
659-
assert!(!type_needs_drop(bcx.tcx(), self.ty));
660+
assert!(!bcx.fcx.type_needs_drop(self.ty));
660661
assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue);
661662
if self.kind.is_by_ref() {
662663
load_ty(bcx, self.val, self.ty)

src/librustc_trans/trans/expr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
974974
let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
975975
let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &**dst, "assign"));
976976

977-
if type_needs_drop(bcx.tcx(), dst_datum.ty) {
977+
if bcx.fcx.type_needs_drop(dst_datum.ty) {
978978
// If there are destructors involved, make sure we
979979
// are copying from an rvalue, since that cannot possible
980980
// alias an lvalue. We are concerned about code like:
@@ -1498,7 +1498,7 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
14981498
assert_eq!(discr, 0);
14991499

15001500
match ty::expr_kind(bcx.tcx(), &*base.expr) {
1501-
ty::RvalueDpsExpr | ty::RvalueDatumExpr if !type_needs_drop(bcx.tcx(), ty) => {
1501+
ty::RvalueDpsExpr | ty::RvalueDatumExpr if !bcx.fcx.type_needs_drop(ty) => {
15021502
bcx = trans_into(bcx, &*base.expr, SaveIn(addr));
15031503
},
15041504
ty::RvalueStmtExpr => bcx.tcx().sess.bug("unexpected expr kind for struct base expr"),
@@ -2116,7 +2116,7 @@ fn trans_assign_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
21162116

21172117
// Evaluate LHS (destination), which should be an lvalue
21182118
let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op"));
2119-
assert!(!type_needs_drop(bcx.tcx(), dst_datum.ty));
2119+
assert!(!bcx.fcx.type_needs_drop(dst_datum.ty));
21202120
let dst_ty = dst_datum.ty;
21212121
let dst = load_ty(bcx, dst_datum.val, dst_datum.ty);
21222122

src/librustc_trans/trans/glue.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
9999
if !type_is_sized(tcx, t) {
100100
return t
101101
}
102+
103+
// FIXME (#22815): note that type_needs_drop conservatively
104+
// approximates in some cases and may say a type expression
105+
// requires drop glue when it actually does not.
106+
//
107+
// (In this case it is not clear whether any harm is done, i.e.
108+
// erroneously returning `t` in some cases where we could have
109+
// returned `tcx.types.i8` does not appear unsound. The impact on
110+
// code quality is unknown at this time.)
111+
102112
if !type_needs_drop(tcx, t) {
103113
return tcx.types.i8;
104114
}
@@ -125,7 +135,7 @@ pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
125135
// NB: v is an *alias* of type t here, not a direct value.
126136
debug!("drop_ty(t={})", t.repr(bcx.tcx()));
127137
let _icx = push_ctxt("drop_ty");
128-
if type_needs_drop(bcx.tcx(), t) {
138+
if bcx.fcx.type_needs_drop(t) {
129139
let ccx = bcx.ccx();
130140
let glue = get_drop_glue(ccx, t);
131141
let glue_type = get_drop_glue_type(ccx, t);
@@ -480,7 +490,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: Ty<'tcx>)
480490
},
481491
_ => {
482492
assert!(type_is_sized(bcx.tcx(), t));
483-
if type_needs_drop(bcx.tcx(), t) && ty::type_is_structural(t) {
493+
if bcx.fcx.type_needs_drop(t) && ty::type_is_structural(t) {
484494
iter_structural_ty(bcx,
485495
v0,
486496
t,

src/librustc_trans/trans/intrinsic.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
156156
let ccx = fcx.ccx;
157157
let tcx = bcx.tcx();
158158

159+
let _icx = push_ctxt("trans_intrinsic_call");
160+
159161
let ret_ty = match callee_ty.sty {
160162
ty::ty_bare_fn(_, ref f) => {
161163
ty::erase_late_bound_regions(bcx.tcx(), &f.sig.output())
@@ -376,7 +378,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
376378
}
377379
(_, "needs_drop") => {
378380
let tp_ty = *substs.types.get(FnSpace, 0);
379-
C_bool(ccx, type_needs_drop(ccx.tcx(), tp_ty))
381+
382+
C_bool(ccx, bcx.fcx.type_needs_drop(tp_ty))
380383
}
381384
(_, "owns_managed") => {
382385
let tp_ty = *substs.types.get(FnSpace, 0);

src/librustc_trans/trans/meth.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
454454
let self_datum = unpack_datum!(
455455
bcx, expr::trans(bcx, self_expr));
456456

457-
let llval = if type_needs_drop(bcx.tcx(), self_datum.ty) {
457+
let llval = if bcx.fcx.type_needs_drop(self_datum.ty) {
458458
let self_datum = unpack_datum!(
459459
bcx, self_datum.to_rvalue_datum(bcx, "trait_callee"));
460460

src/librustc_trans/trans/tvec.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,10 @@ pub fn make_drop_glue_unboxed<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
5353
let not_null = IsNotNull(bcx, vptr);
5454
with_cond(bcx, not_null, |bcx| {
5555
let ccx = bcx.ccx();
56-
let tcx = bcx.tcx();
5756
let _icx = push_ctxt("tvec::make_drop_glue_unboxed");
5857

5958
let dataptr = get_dataptr(bcx, vptr);
60-
let bcx = if type_needs_drop(tcx, unit_ty) {
59+
let bcx = if bcx.fcx.type_needs_drop(unit_ty) {
6160
let len = get_len(bcx, vptr);
6261
iter_vec_raw(bcx,
6362
dataptr,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2015 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+
// Regression test for Issue #22536: If a type implements Copy, then
12+
// moving it must not zero the original memory.
13+
14+
trait Resources {
15+
type Buffer: Copy;
16+
fn foo(&self) {}
17+
}
18+
19+
struct BufferHandle<R: Resources> {
20+
raw: <R as Resources>::Buffer,
21+
}
22+
impl<R: Resources> Copy for BufferHandle<R> {}
23+
24+
enum Res {}
25+
impl Resources for Res {
26+
type Buffer = u32;
27+
}
28+
impl Copy for Res { }
29+
30+
fn main() {
31+
let b: BufferHandle<Res> = BufferHandle { raw: 1 };
32+
let c = b;
33+
assert_eq!(c.raw, b.raw)
34+
}

0 commit comments

Comments
 (0)