about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_lint/builtin.rs54
-rw-r--r--src/librustc_lint/lib.rs1
-rw-r--r--src/test/compile-fail/lint-no-drop-on-repr-extern.rs59
3 files changed, 114 insertions, 0 deletions
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 1d5c5fb86cb..cc2c9b735ea 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -2142,3 +2142,57 @@ impl LintPass for UnstableFeatures {
         }
     }
 }
+
+/// Lints for attempts to impl Drop on types that have `#[repr(C)]`
+/// attribute (see issue #24585).
+#[derive(Copy, Clone)]
+pub struct DropWithReprExtern;
+
+declare_lint! {
+    DROP_WITH_REPR_EXTERN,
+    Warn,
+    "use of #[repr(C)] on a type that implements Drop"
+}
+
+impl LintPass for DropWithReprExtern {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(DROP_WITH_REPR_EXTERN)
+    }
+    fn check_crate(&mut self, ctx: &Context, _: &ast::Crate) {
+        for dtor_did in ctx.tcx.destructors.borrow().iter() {
+            let (drop_impl_did, dtor_self_type) =
+                if dtor_did.krate == ast::LOCAL_CRATE {
+                    let impl_did = ctx.tcx.map.get_parent_did(dtor_did.node);
+                    let ty = ty::lookup_item_type(ctx.tcx, impl_did).ty;
+                    (impl_did, ty)
+                } else {
+                    continue;
+                };
+
+            match dtor_self_type.sty {
+                ty::ty_enum(self_type_did, _) |
+                ty::ty_struct(self_type_did, _) |
+                ty::ty_closure(self_type_did, _) => {
+                    let hints = ty::lookup_repr_hints(ctx.tcx, self_type_did);
+                    if hints.iter().any(|attr| *attr == attr::ReprExtern) &&
+                        ty::ty_dtor(ctx.tcx, self_type_did).has_drop_flag() {
+                        let drop_impl_span = ctx.tcx.map.def_id_span(drop_impl_did,
+                                                                     codemap::DUMMY_SP);
+                        let self_defn_span = ctx.tcx.map.def_id_span(self_type_did,
+                                                                     codemap::DUMMY_SP);
+                        ctx.span_lint(DROP_WITH_REPR_EXTERN,
+                                      drop_impl_span,
+                                      "implementing Drop adds hidden state to types, \
+                                       possibly conflicting with `#[repr(C)]`");
+                        // FIXME #19668: could be span_lint_note instead of manual guard.
+                        if ctx.current_level(DROP_WITH_REPR_EXTERN) != Level::Allow {
+                            ctx.sess().span_note(self_defn_span,
+                                               "the `#[repr(C)]` attribute is attached here");
+                        }
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
+}
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 544fe759819..970f9c634a2 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -108,6 +108,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
                  UnconditionalRecursion,
                  InvalidNoMangleItems,
                  PluginAsLibrary,
+                 DropWithReprExtern,
                  );
 
     add_builtin_with_new!(sess,
diff --git a/src/test/compile-fail/lint-no-drop-on-repr-extern.rs b/src/test/compile-fail/lint-no-drop-on-repr-extern.rs
new file mode 100644
index 00000000000..2df57b08f28
--- /dev/null
+++ b/src/test/compile-fail/lint-no-drop-on-repr-extern.rs
@@ -0,0 +1,59 @@
+// Copyright 2015 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.
+
+// Check we reject structs that mix a `Drop` impl with `#[repr(C)]`.
+//
+// As a special case, also check that we do not warn on such structs
+// if they also are declared with `#[unsafe_no_drop_flag]`
+
+#![feature(unsafe_no_drop_flag)]
+#![deny(drop_with_repr_extern)]
+
+#[repr(C)] struct As { x: Box<i8> }
+#[repr(C)] enum Ae { Ae(Box<i8>), _None }
+
+struct Bs { x: Box<i8> }
+enum Be { Be(Box<i8>), _None }
+
+#[repr(C)] struct Cs { x: Box<i8> }
+//~^ NOTE the `#[repr(C)]` attribute is attached here
+
+impl Drop for Cs { fn drop(&mut self) { } }
+//~^ ERROR implementing Drop adds hidden state to types, possibly conflicting with `#[repr(C)]`
+
+#[repr(C)] enum Ce { Ce(Box<i8>), _None }
+//~^ NOTE the `#[repr(C)]` attribute is attached here
+
+impl Drop for Ce { fn drop(&mut self) { } }
+//~^ ERROR implementing Drop adds hidden state to types, possibly conflicting with `#[repr(C)]`
+
+#[unsafe_no_drop_flag]
+#[repr(C)] struct Ds { x: Box<i8> }
+
+impl Drop for Ds { fn drop(&mut self) { } }
+
+#[unsafe_no_drop_flag]
+#[repr(C)] enum De { De(Box<i8>), _None }
+
+impl Drop for De { fn drop(&mut self) { } }
+
+fn main() {
+    let a = As { x: Box::new(3) };
+    let b = Bs { x: Box::new(3) };
+    let c = Cs { x: Box::new(3) };
+    let d = Ds { x: Box::new(3) };
+
+    println!("{:?}", (*a.x, *b.x, *c.x, *d.x));
+
+    let _a = Ae::Ae(Box::new(3));
+    let _b = Be::Be(Box::new(3));
+    let _c = Ce::Ce(Box::new(3));
+    let _d = De::De(Box::new(3));
+}