From c20717ef1d04680d5d4df676dd8479d092801374 Mon Sep 17 00:00:00 2001
From: Tyler Mandry <tmandry@gmail.com>
Date: Sun, 21 Jul 2019 20:58:55 -0700
Subject: [PATCH] Lower core::ops::drop directly to MIR drop

This change causes drop() to always drop in-place. This is meant to
allow internal optimizations (see #62508).

This does _not_ change the documented contract for drop(). Only internal
compiler code is allowed to rely on this for now.
---
 src/libcore/mem/mod.rs               |  1 +
 src/libcore/ops/drop.rs              |  3 +-
 src/librustc/middle/lang_items.rs    |  3 +-
 src/librustc_mir/build/expr/into.rs  | 67 +++++++++++++++++++---------
 src/test/mir-opt/box_expr.rs         |  2 +-
 src/test/mir-opt/issue-49232.rs      |  2 +-
 src/test/ui/type_length_limit.rs     |  4 +-
 src/test/ui/type_length_limit.stderr |  8 ++--
 8 files changed, 60 insertions(+), 30 deletions(-)

diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs
index f2729168763bb..8871f667dcf28 100644
--- a/src/libcore/mem/mod.rs
+++ b/src/libcore/mem/mod.rs
@@ -693,6 +693,7 @@ pub fn replace<T>(dest: &mut T, mut src: T) -> T {
 /// [`Copy`]: ../../std/marker/trait.Copy.html
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), lang = "drop")]
 pub fn drop<T>(_x: T) { }
 
 /// Interprets `src` as having type `&U`, and then reads `src` without moving
diff --git a/src/libcore/ops/drop.rs b/src/libcore/ops/drop.rs
index eae63ea2390a8..2bd3234f5ae09 100644
--- a/src/libcore/ops/drop.rs
+++ b/src/libcore/ops/drop.rs
@@ -80,7 +80,8 @@
 ///     let _second = PrintOnDrop("Declared second!");
 /// }
 /// ```
-#[lang = "drop"]
+#[cfg_attr(not(bootstrap), lang = "drop_trait")]
+#[cfg_attr(bootstrap, lang = "drop")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Drop {
     /// Executes the destructor for this type.
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index bdd48b3447498..1d94b165cacec 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -279,7 +279,7 @@ language_item_table! {
     SyncTraitLangItem,           "sync",               sync_trait,              Target::Trait;
     FreezeTraitLangItem,         "freeze",             freeze_trait,            Target::Trait;
 
-    DropTraitLangItem,           "drop",               drop_trait,              Target::Trait;
+    DropTraitLangItem,           "drop_trait",         drop_trait,              Target::Trait;
 
     CoerceUnsizedTraitLangItem,  "coerce_unsized",     coerce_unsized_trait,    Target::Trait;
     DispatchFromDynTraitLangItem,"dispatch_from_dyn",  dispatch_from_dyn_trait, Target::Trait;
@@ -349,6 +349,7 @@ language_item_table! {
 
     ExchangeMallocFnLangItem,    "exchange_malloc",    exchange_malloc_fn,      Target::Fn;
     BoxFreeFnLangItem,           "box_free",           box_free_fn,             Target::Fn;
+    DropFnLangItem,              "drop",               drop_fn,                 Target::Fn;
     DropInPlaceFnLangItem,       "drop_in_place",      drop_in_place_fn,        Target::Fn;
     OomLangItem,                 "oom",                oom,                     Target::Fn;
     AllocLayoutLangItem,         "alloc_layout",       alloc_layout,            Target::Struct;
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index e433da904a678..4fb5650e7d725 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -191,16 +191,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 exit_block.unit()
             }
             ExprKind::Call { ty, fun, args, from_hir_call } => {
-                let intrinsic = match ty.sty {
+                let (fn_def_id, intrinsic) = match ty.sty {
                     ty::FnDef(def_id, _) => {
                         let f = ty.fn_sig(this.hir.tcx());
                         if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
-                            Some(this.hir.tcx().item_name(def_id).as_str())
+                            (Some(def_id), Some(this.hir.tcx().item_name(def_id).as_str()))
                         } else {
-                            None
+                            (Some(def_id), None)
                         }
                     }
-                    _ => None,
+                    _ => (None, None),
                 };
                 let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
                 let fun = unpack!(block = this.as_local_operand(block, fun));
@@ -237,26 +237,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         .map(|arg| unpack!(block = this.as_local_operand(block, arg)))
                         .collect();
 
+                    let drop_location = if fn_def_id.is_some()
+                        && this.hir.tcx().lang_items().drop_fn() == fn_def_id
+                    {
+                        assert_eq!(args.len(), 1, "drop() must have exactly one argument");
+                        match &args[0] {
+                            Operand::Move(place) => Some(place.clone()),
+                            _ => None,
+                        }
+                    } else {
+                        None
+                    };
+
                     let success = this.cfg.start_new_block();
                     let cleanup = this.diverge_cleanup();
-                    this.cfg.terminate(
-                        block,
-                        source_info,
-                        TerminatorKind::Call {
-                            func: fun,
-                            args,
-                            cleanup: Some(cleanup),
-                            // FIXME(varkor): replace this with an uninhabitedness-based check.
-                            // This requires getting access to the current module to call
-                            // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
-                            destination: if expr.ty.is_never() {
-                                None
-                            } else {
-                                Some((destination.clone(), success))
+
+                    if let Some(location) = drop_location {
+                        this.cfg.terminate(
+                            block,
+                            source_info,
+                            TerminatorKind::Drop {
+                                location,
+                                target: success,
+                                unwind: Some(cleanup)
                             },
-                            from_hir_call,
-                        },
-                    );
+                        );
+                    } else {
+                        this.cfg.terminate(
+                            block,
+                            source_info,
+                            TerminatorKind::Call {
+                                func: fun,
+                                args,
+                                cleanup: Some(cleanup),
+                                // FIXME(varkor): replace this with an uninhabitedness-based check.
+                                // This requires getting access to the current module to call
+                                // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
+                                destination: if expr.ty.is_never() {
+                                    None
+                                } else {
+                                    Some((destination.clone(), success))
+                                },
+                                from_hir_call,
+                            },
+                        );
+                    }
                     success.unit()
                 }
             }
diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs
index d9fa3d3d4736d..206b54d471d26 100644
--- a/src/test/mir-opt/box_expr.rs
+++ b/src/test/mir-opt/box_expr.rs
@@ -53,7 +53,7 @@ impl Drop for S {
 //         StorageLive(_3);
 //         StorageLive(_4);
 //         _4 = move _1;
-//         _3 = const std::mem::drop::<std::boxed::Box<S>>(move _4) -> [return: bb5, unwind: bb7];
+//         drop(_4) -> [return: bb5, unwind: bb7];
 //     }
 //
 //     bb5: {
diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs
index d0dbcbd7515f8..a7e8efbaa5270 100644
--- a/src/test/mir-opt/issue-49232.rs
+++ b/src/test/mir-opt/issue-49232.rs
@@ -76,7 +76,7 @@ fn main() {
 //         StorageLive(_5);
 //         StorageLive(_6);
 //         _6 = &_2;
-//         _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4];
+//         drop(_6) -> [return: bb13, unwind: bb4];
 //     }
 //     bb13: {
 //         StorageDead(_6);
diff --git a/src/test/ui/type_length_limit.rs b/src/test/ui/type_length_limit.rs
index cd15f81a61535..7d4d40a885bd7 100644
--- a/src/test/ui/type_length_limit.rs
+++ b/src/test/ui/type_length_limit.rs
@@ -22,6 +22,8 @@ link! { F, G }
 
 pub struct G;
 
+fn take<T>(x: T) {}
+
 fn main() {
-    drop::<Option<A>>(None);
+    take::<Option<A>>(None);
 }
diff --git a/src/test/ui/type_length_limit.stderr b/src/test/ui/type_length_limit.stderr
index 7e308f107ba00..532e6bd272bde 100644
--- a/src/test/ui/type_length_limit.stderr
+++ b/src/test/ui/type_length_limit.stderr
@@ -1,8 +1,8 @@
-error: reached the type-length limit while instantiating `std::mem::drop::<std::option::Op... G), (G, G, G), (G, G, G))))))>>`
-  --> $SRC_DIR/libcore/mem/mod.rs:LL:COL
+error: reached the type-length limit while instantiating `take::<std::option::Option<(((((... G), (G, G, G), (G, G, G))))))>>`
+  --> $DIR/type_length_limit.rs:25:1
    |
-LL | pub fn drop<T>(_x: T) { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn take<T>(x: T) {}
+   | ^^^^^^^^^^^^^^^^^^^
    |
    = note: consider adding a `#![type_length_limit="1094"]` attribute to your crate