about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_lint/src/traits.rs63
-rw-r--r--src/test/ui/dyn-drop/dyn-drop.rs16
-rw-r--r--src/test/ui/dyn-drop/dyn-drop.stderr38
3 files changed, 116 insertions, 1 deletions
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index e632f29e672..e713ce7c71b 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -37,10 +37,47 @@ declare_lint! {
     "bounds of the form `T: Drop` are useless"
 }
 
+declare_lint! {
+    /// The `dyn_drop` lint checks for trait objects with `std::ops::Drop`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn foo(_x: Box<dyn Drop>) {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// A trait object bound of the form `dyn Drop` is most likely misleading
+    /// and not what the programmer intended.
+    ///
+    /// `Drop` bounds do not actually indicate whether a type can be trivially
+    /// dropped or not, because a composite type containing `Drop` types does
+    /// not necessarily implement `Drop` itself. Naïvely, one might be tempted
+    /// to write a deferred drop system, to pull cleaning up memory out of a
+    /// latency-sensitive code path, using `dyn Drop` trait objects. However,
+    /// this breaks down e.g. when `T` is `String`, which does not implement
+    /// `Drop`, but should probably be accepted.
+    ///
+    /// To write a trait object bound that accepts anything, use a placeholder
+    /// trait with a blanket implementation.
+    ///
+    /// ```rust
+    /// trait Placeholder {}
+    /// impl<T> Placeholder for T {}
+    /// fn foo(_x: Box<dyn Placeholder>) {}
+    /// ```
+    pub DYN_DROP,
+    Warn,
+    "trait objects of the form `dyn Drop` are useless"
+}
+
 declare_lint_pass!(
     /// Lint for bounds of the form `T: Drop`, which usually
     /// indicate an attempt to emulate `std::mem::needs_drop`.
-    DropTraitConstraints => [DROP_BOUNDS]
+    DropTraitConstraints => [DROP_BOUNDS, DYN_DROP]
 );
 
 impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
@@ -75,4 +112,28 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
             }
         }
     }
+
+    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
+        let bounds = match &ty.kind {
+            hir::TyKind::TraitObject(bounds, _lifetime, _syntax) => bounds,
+            _ => return,
+        };
+        for bound in &bounds[..] {
+            let def_id = bound.trait_ref.trait_def_id();
+            if cx.tcx.lang_items().drop_trait() == def_id {
+                cx.struct_span_lint(DYN_DROP, bound.span, |lint| {
+                    let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) {
+                        Some(needs_drop) => needs_drop,
+                        None => return,
+                    };
+                    let msg = format!(
+                        "types that do not implement `Drop` can still have drop glue, consider \
+                        instead using `{}` to detect whether a type is trivially dropped",
+                        cx.tcx.def_path_str(needs_drop)
+                    );
+                    lint.build(&msg).emit()
+                });
+            }
+        }
+    }
 }
diff --git a/src/test/ui/dyn-drop/dyn-drop.rs b/src/test/ui/dyn-drop/dyn-drop.rs
new file mode 100644
index 00000000000..e1668a3f188
--- /dev/null
+++ b/src/test/ui/dyn-drop/dyn-drop.rs
@@ -0,0 +1,16 @@
+#![deny(dyn_drop)]
+#![allow(bare_trait_objects)]
+fn foo(_: Box<dyn Drop>) {} //~ ERROR
+fn bar(_: &dyn Drop) {} //~ERROR
+fn baz(_: *mut Drop) {} //~ ERROR
+struct Foo {
+  _x: Box<dyn Drop> //~ ERROR
+}
+trait Bar {
+  type T: ?Sized;
+}
+struct Baz {}
+impl Bar for Baz {
+  type T = dyn Drop; //~ ERROR
+}
+fn main() {}
diff --git a/src/test/ui/dyn-drop/dyn-drop.stderr b/src/test/ui/dyn-drop/dyn-drop.stderr
new file mode 100644
index 00000000000..1b1dbc4d12d
--- /dev/null
+++ b/src/test/ui/dyn-drop/dyn-drop.stderr
@@ -0,0 +1,38 @@
+error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped
+  --> $DIR/dyn-drop.rs:3:19
+   |
+LL | fn foo(_: Box<dyn Drop>) {}
+   |                   ^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/dyn-drop.rs:1:9
+   |
+LL | #![deny(dyn_drop)]
+   |         ^^^^^^^^
+
+error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped
+  --> $DIR/dyn-drop.rs:4:16
+   |
+LL | fn bar(_: &dyn Drop) {}
+   |                ^^^^
+
+error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped
+  --> $DIR/dyn-drop.rs:5:16
+   |
+LL | fn baz(_: *mut Drop) {}
+   |                ^^^^
+
+error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped
+  --> $DIR/dyn-drop.rs:7:15
+   |
+LL |   _x: Box<dyn Drop>
+   |               ^^^^
+
+error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped
+  --> $DIR/dyn-drop.rs:14:16
+   |
+LL |   type T = dyn Drop;
+   |                ^^^^
+
+error: aborting due to 5 previous errors
+