about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2017-07-22 15:18:52 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2017-08-28 09:27:05 +0300
commit9b61771ea80b476eb92bda6388fcf6f0f88e8567 (patch)
tree5f727c1876d45c500443ee0691fd2290fa0010c7
parentb8c05fe90bc132f707107e6926c991437015d34f (diff)
downloadrust-9b61771ea80b476eb92bda6388fcf6f0f88e8567.tar.gz
rust-9b61771ea80b476eb92bda6388fcf6f0f88e8567.zip
rustc_mir: conservatively deny non-noop drops in constant contexts.
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs60
-rw-r--r--src/test/compile-fail/check-static-values-constraints.rs3
-rw-r--r--src/test/compile-fail/static-drop-scope.rs26
3 files changed, 83 insertions, 6 deletions
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index ee99bb7d9d5..05ba517e263 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -120,6 +120,7 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     return_qualif: Option<Qualif>,
     qualif: Qualif,
     const_fn_arg_vars: BitVector,
+    local_needs_drop: IndexVec<Local, Option<Span>>,
     temp_promotion_state: IndexVec<Local, TempState>,
     promotion_candidates: Vec<Candidate>
 }
@@ -146,6 +147,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             return_qualif: None,
             qualif: Qualif::empty(),
             const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
+            local_needs_drop: IndexVec::from_elem(None, &mir.local_decls),
             temp_promotion_state: temps,
             promotion_candidates: vec![]
         }
@@ -194,15 +196,25 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
     }
 
     /// Check for NEEDS_DROP (from an ADT or const fn call) and
+    /// error, unless we're in a function.
+    fn always_deny_drop(&self) {
+        self.deny_drop_with_feature_gate_override(false);
+    }
+
+    /// Check for NEEDS_DROP (from an ADT or const fn call) and
     /// error, unless we're in a function, or the feature-gate
     /// for globals with destructors is enabled.
     fn deny_drop(&self) {
+        self.deny_drop_with_feature_gate_override(true);
+    }
+
+    fn deny_drop_with_feature_gate_override(&self, allow_gate: bool) {
         if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) {
             return;
         }
 
         // Static and const fn's allow destructors, but they're feature-gated.
-        let msg = if self.mode != Mode::Const {
+        let msg = if allow_gate && self.mode != Mode::Const {
             // Feature-gate for globals with destructors is enabled.
             if self.tcx.sess.features.borrow().drop_types_in_const {
                 return;
@@ -223,7 +235,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
         let mut err =
             struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg);
 
-        if self.mode != Mode::Const {
+        if allow_gate && self.mode != Mode::Const {
             help!(&mut err,
                   "in Nightly builds, add `#![feature(drop_types_in_const)]` \
                    to the crate attributes to enable");
@@ -231,7 +243,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             self.find_drop_implementation_method_span()
                 .map(|span| err.span_label(span, "destructor defined here"));
 
-            err.span_label(self.span, "constants cannot have destructors");
+            err.span_label(self.span,
+                format!("{}s cannot have destructors", self.mode));
         }
 
         err.emit();
@@ -314,6 +327,15 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             return;
         }
 
+        // When initializing a local, record whether the *value* being
+        // stored in it needs dropping, which it may not, even if its
+        // type does, e.g. `None::<String>`.
+        if let Lvalue::Local(local) = *dest {
+            if qualif.intersects(Qualif::NEEDS_DROP) {
+                self.local_needs_drop[local] = Some(self.span);
+            }
+        }
+
         match *dest {
             Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
                 debug!("store to temp {:?}", index);
@@ -360,7 +382,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
 
             let target = match mir[bb].terminator().kind {
                 TerminatorKind::Goto { target } |
-                // Drops are considered noops.
                 TerminatorKind::Drop { target, .. } |
                 TerminatorKind::Assert { target, .. } |
                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
@@ -558,11 +579,16 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
         match *operand {
-            Operand::Consume(_) => {
+            Operand::Consume(ref lvalue) => {
                 self.nest(|this| {
                     this.super_operand(operand, location);
                     this.try_consume();
                 });
+
+                // Mark the consumed locals to indicate later drops are noops.
+                if let Lvalue::Local(local) = *lvalue {
+                    self.local_needs_drop[local] = None;
+                }
             }
             Operand::Constant(ref constant) => {
                 if let Literal::Item { def_id, substs } = constant.literal {
@@ -864,6 +890,30 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 }
                 self.assign(dest, location);
             }
+        } else if let TerminatorKind::Drop { location: ref lvalue, .. } = *kind {
+            self.super_terminator_kind(bb, kind, location);
+
+            // Deny *any* live drops anywhere other than functions.
+            if self.mode != Mode::Fn {
+                // HACK(eddyb) Emulate a bit of dataflow analysis,
+                // conservatively, that drop elaboration will do.
+                let needs_drop = if let Lvalue::Local(local) = *lvalue {
+                    self.local_needs_drop[local]
+                } else {
+                    None
+                };
+
+                if let Some(span) = needs_drop {
+                    let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
+                    self.add_type(ty);
+
+                    // Use the original assignment span to be more precise.
+                    let old_span = self.span;
+                    self.span = span;
+                    self.always_deny_drop();
+                    self.span = old_span;
+                }
+            }
         } else {
             // Qualify any operands inside other terminators.
             self.super_terminator_kind(bb, kind, location);
diff --git a/src/test/compile-fail/check-static-values-constraints.rs b/src/test/compile-fail/check-static-values-constraints.rs
index 3642add3259..c349aababd6 100644
--- a/src/test/compile-fail/check-static-values-constraints.rs
+++ b/src/test/compile-fail/check-static-values-constraints.rs
@@ -86,8 +86,9 @@ static STATIC8: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
 // This example should fail because field1 in the base struct is not safe
 static STATIC9: SafeStruct = SafeStruct{field1: SafeEnum::Variant1,
                                         ..SafeStruct{field1: SafeEnum::Variant3(WithDtor),
+//~^ ERROR destructors in statics are an unstable feature
+//~| ERROR statics are not allowed to have destructors
                                                      field2: SafeEnum::Variant1}};
-//~^^ ERROR destructors in statics are an unstable feature
 
 struct UnsafeStruct;
 
diff --git a/src/test/compile-fail/static-drop-scope.rs b/src/test/compile-fail/static-drop-scope.rs
new file mode 100644
index 00000000000..e5f10b65cee
--- /dev/null
+++ b/src/test/compile-fail/static-drop-scope.rs
@@ -0,0 +1,26 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(drop_types_in_const)]
+
+struct WithDtor;
+
+impl Drop for WithDtor {
+    fn drop(&mut self) {}
+}
+
+static FOO: Option<&'static WithDtor> = Some(&WithDtor);
+//~^ ERROR statics are not allowed to have destructors
+//~| ERROR borrowed value does not live long enoug
+
+static BAR: i32 = (WithDtor, 0).1;
+//~^ ERROR statics are not allowed to have destructors
+
+fn main () {}