Skip to content

Commit 33c8992

Browse files
committed
Auto merge of #34109 - pnkfelix:fix-issue-34101, r=arielb1
Fix issue #34101 Fix issue #34101: do not track subcontent of type with dtor nor gather flags for untracked content. (Includes a regression test, which needed to go into `compile-fail/` due to weaknesses when combining `#[deny(warnings)]` with `tcx.sess.span_warn(..)`)
2 parents 24526cc + 4b6a68e commit 33c8992

File tree

3 files changed

+103
-28
lines changed

3 files changed

+103
-28
lines changed

src/librustc_borrowck/borrowck/mir/elaborate_drops.rs

+7-17
Original file line numberDiff line numberDiff line change
@@ -197,31 +197,21 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
197197
}
198198

199199
/// Returns whether this lvalue is tracked by drop elaboration. This
200-
/// includes all lvalues, except these behind references or arrays.
201-
///
202-
/// Lvalues behind references or arrays are not tracked by elaboration
203-
/// and are always assumed to be initialized when accessible. As
204-
/// references and indexes can be reseated, trying to track them
205-
/// can only lead to trouble.
200+
/// includes all lvalues, except these (1.) behind references or arrays,
201+
/// or (2.) behind ADT's with a Drop impl.
206202
fn lvalue_is_tracked(&self, lv: &Lvalue<'tcx>) -> bool
207203
{
204+
// `lvalue_contents_drop_state_cannot_differ` only compares
205+
// the `lv` to its immediate contents, while this recursively
206+
// follows parent chain formed by `base` of each projection.
208207
if let &Lvalue::Projection(ref data) = lv {
209-
self.lvalue_contents_are_tracked(&data.base)
208+
!super::lvalue_contents_drop_state_cannot_differ(self.tcx, self.mir, &data.base) &&
209+
self.lvalue_is_tracked(&data.base)
210210
} else {
211211
true
212212
}
213213
}
214214

215-
fn lvalue_contents_are_tracked(&self, lv: &Lvalue<'tcx>) -> bool {
216-
let ty = self.mir.lvalue_ty(self.tcx, lv).to_ty(self.tcx);
217-
match ty.sty {
218-
ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
219-
false
220-
}
221-
_ => self.lvalue_is_tracked(lv)
222-
}
223-
}
224-
225215
fn collect_drop_flags(&mut self)
226216
{
227217
for bb in self.mir.all_basic_blocks() {

src/librustc_borrowck/borrowck/mir/mod.rs

+40-11
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,45 @@ fn move_path_children_matching<'tcx, F>(move_paths: &MovePathData<'tcx>,
235235
None
236236
}
237237

238+
/// When enumerating the child fragments of a path, don't recurse into
239+
/// paths (1.) past arrays, slices, and pointers, nor (2.) into a type
240+
/// that implements `Drop`.
241+
///
242+
/// Lvalues behind references or arrays are not tracked by elaboration
243+
/// and are always assumed to be initialized when accessible. As
244+
/// references and indexes can be reseated, trying to track them can
245+
/// only lead to trouble.
246+
///
247+
/// Lvalues behind ADT's with a Drop impl are not tracked by
248+
/// elaboration since they can never have a drop-flag state that
249+
/// differs from that of the parent with the Drop impl.
250+
///
251+
/// In both cases, the contents can only be accessed if and only if
252+
/// their parents are initialized. This implies for example that there
253+
/// is no need to maintain separate drop flags to track such state.
254+
///
255+
/// FIXME: we have to do something for moving slice patterns.
256+
fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
257+
mir: &Mir<'tcx>,
258+
lv: &repr::Lvalue<'tcx>) -> bool {
259+
let ty = mir.lvalue_ty(tcx, lv).to_ty(tcx);
260+
match ty.sty {
261+
ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
262+
debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => false",
263+
lv, ty);
264+
true
265+
}
266+
ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor() => {
267+
debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => false",
268+
lv, ty);
269+
true
270+
}
271+
_ => {
272+
false
273+
}
274+
}
275+
}
276+
238277
fn on_all_children_bits<'a, 'tcx, F>(
239278
tcx: TyCtxt<'a, 'tcx, 'tcx>,
240279
mir: &Mir<'tcx>,
@@ -251,17 +290,7 @@ fn on_all_children_bits<'a, 'tcx, F>(
251290
{
252291
match move_data.move_paths[path].content {
253292
MovePathContent::Lvalue(ref lvalue) => {
254-
match mir.lvalue_ty(tcx, lvalue).to_ty(tcx).sty {
255-
// don't trace paths past arrays, slices, and
256-
// pointers. They can only be accessed while
257-
// their parents are initialized.
258-
//
259-
// FIXME: we have to do something for moving
260-
// slice patterns.
261-
ty::TyArray(..) | ty::TySlice(..) |
262-
ty::TyRef(..) | ty::TyRawPtr(..) => true,
263-
_ => false
264-
}
293+
lvalue_contents_drop_state_cannot_differ(tcx, mir, lvalue)
265294
}
266295
_ => true
267296
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
// Issue 34101: Circa 2016-06-05, `fn inline` below issued an
12+
// erroneous warning from the elaborate_drops pass about moving out of
13+
// a field in `Foo`, which has a destructor (and thus cannot have
14+
// content moved out of it). The reason that the warning is erroneous
15+
// in this case is that we are doing a *replace*, not a move, of the
16+
// content in question, and it is okay to replace fields within `Foo`.
17+
//
18+
// Another more subtle problem was that the elaborate_drops was
19+
// creating a separate drop flag for that internally replaced content,
20+
// even though the compiler should enforce an invariant that any drop
21+
// flag for such subcontent of `Foo` will always have the same value
22+
// as the drop flag for `Foo` itself.
23+
//
24+
// This test is structured in a funny way; we cannot test for emission
25+
// of the warning in question via the lint system, and therefore
26+
// `#![deny(warnings)]` does nothing to detect it.
27+
//
28+
// So instead we use `#[rustc_error]` and put the test into
29+
// `compile_fail`, where the emitted warning *will* be caught.
30+
31+
#![feature(rustc_attrs)]
32+
33+
struct Foo(String);
34+
35+
impl Drop for Foo {
36+
fn drop(&mut self) {}
37+
}
38+
39+
fn inline() {
40+
// (dummy variable so `f` gets assigned `var1` in MIR for both fn's)
41+
let _s = ();
42+
let mut f = Foo(String::from("foo"));
43+
f.0 = String::from("bar");
44+
}
45+
46+
fn outline() {
47+
let _s = String::from("foo");
48+
let mut f = Foo(_s);
49+
f.0 = String::from("bar");
50+
}
51+
52+
#[rustc_error]
53+
fn main() { //~ ERROR compilation successful
54+
inline();
55+
outline();
56+
}

0 commit comments

Comments
 (0)