From f6e5ac640be7ca71f389b6842cfeb83c24bd15c7 Mon Sep 17 00:00:00 2001
From: Eduard-Mihai Burtescu <edy.burt@gmail.com>
Date: Mon, 1 Jul 2019 20:06:30 +0300
Subject: [PATCH] rustc_mir: treat DropAndReplace as Drop + Assign in
 qualify_consts.

---
 src/librustc_mir/transform/qualify_consts.rs  | 23 +++++++++++++---
 src/test/ui/consts/const-eval/const_let.rs    | 15 +++++++++--
 .../ui/consts/const-eval/const_let.stderr     | 27 +++++++++++++------
 3 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index f082b5e5a046c..72180d60bd6d0 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -142,6 +142,7 @@ impl<'a, 'tcx> ConstCx<'a, 'tcx> {
 #[derive(Copy, Clone, Debug)]
 enum ValueSource<'a, 'tcx> {
     Rvalue(&'a Rvalue<'tcx>),
+    DropAndReplace(&'a Operand<'tcx>),
     Call {
         callee: &'a Operand<'tcx>,
         args: &'a [Operand<'tcx>],
@@ -298,6 +299,7 @@ trait Qualif {
     fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
         match source {
             ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
+            ValueSource::DropAndReplace(source) => Self::in_operand(cx, source),
             ValueSource::Call { callee, args, return_ty } => {
                 Self::in_call(cx, callee, args, return_ty)
             }
@@ -889,6 +891,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
             let target = match body[bb].terminator().kind {
                 TerminatorKind::Goto { target } |
                 TerminatorKind::Drop { target, .. } |
+                TerminatorKind::DropAndReplace { target, .. } |
                 TerminatorKind::Assert { target, .. } |
                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
                     Some(target)
@@ -900,7 +903,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                 }
 
                 TerminatorKind::SwitchInt {..} |
-                TerminatorKind::DropAndReplace { .. } |
                 TerminatorKind::Resume |
                 TerminatorKind::Abort |
                 TerminatorKind::GeneratorDrop |
@@ -1393,8 +1395,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
             for arg in args {
                 self.visit_operand(arg, location);
             }
-        } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
-            self.super_terminator_kind(kind, location);
+        } else if let TerminatorKind::Drop {
+            location: ref place, ..
+        } | TerminatorKind::DropAndReplace {
+            location: ref place, ..
+        } = *kind {
+            match *kind {
+                TerminatorKind::DropAndReplace { .. } => {}
+                _ => self.super_terminator_kind(kind, location),
+            }
 
             // Deny *any* live drops anywhere other than functions.
             if self.mode.requires_const_checking() {
@@ -1423,6 +1432,14 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
                     }
                 }
             }
+
+            match *kind {
+                TerminatorKind::DropAndReplace { ref value, .. } => {
+                    self.assign(place, ValueSource::DropAndReplace(value), location);
+                    self.visit_operand(value, location);
+                }
+                _ => {}
+            }
         } else {
             // Qualify any operands inside other terminators.
             self.super_terminator_kind(kind, location);
diff --git a/src/test/ui/consts/const-eval/const_let.rs b/src/test/ui/consts/const-eval/const_let.rs
index 63321b9120076..18692dbced679 100644
--- a/src/test/ui/consts/const-eval/const_let.rs
+++ b/src/test/ui/consts/const-eval/const_let.rs
@@ -9,10 +9,21 @@ impl Drop for FakeNeedsDrop {
 // ok
 const X: FakeNeedsDrop = { let x = FakeNeedsDrop; x };
 
+// ok (used to incorrectly error, see #62273)
+const X2: FakeNeedsDrop = { let x; x = FakeNeedsDrop; x };
+
 // error
 const Y: FakeNeedsDrop = { let mut x = FakeNeedsDrop; x = FakeNeedsDrop; x };
-//~^ ERROR constant contains unimplemented expression type
+//~^ ERROR destructors cannot be evaluated at compile-time
+
+// error
+const Y2: FakeNeedsDrop = { let mut x; x = FakeNeedsDrop; x = FakeNeedsDrop; x };
+//~^ ERROR destructors cannot be evaluated at compile-time
 
 // error
 const Z: () = { let mut x = None; x = Some(FakeNeedsDrop); };
-//~^ ERROR constant contains unimplemented expression type
+//~^ ERROR destructors cannot be evaluated at compile-time
+
+// error
+const Z2: () = { let mut x; x = None; x = Some(FakeNeedsDrop); };
+//~^ ERROR destructors cannot be evaluated at compile-time
diff --git a/src/test/ui/consts/const-eval/const_let.stderr b/src/test/ui/consts/const-eval/const_let.stderr
index 00de97e6fb3a0..0a6a222ae2963 100644
--- a/src/test/ui/consts/const-eval/const_let.stderr
+++ b/src/test/ui/consts/const-eval/const_let.stderr
@@ -1,15 +1,26 @@
-error[E0019]: constant contains unimplemented expression type
-  --> $DIR/const_let.rs:13:55
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/const_let.rs:16:32
    |
 LL | const Y: FakeNeedsDrop = { let mut x = FakeNeedsDrop; x = FakeNeedsDrop; x };
-   |                                                       ^
+   |                                ^^^^^ constants cannot evaluate destructors
 
-error[E0019]: constant contains unimplemented expression type
-  --> $DIR/const_let.rs:17:35
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/const_let.rs:20:33
+   |
+LL | const Y2: FakeNeedsDrop = { let mut x; x = FakeNeedsDrop; x = FakeNeedsDrop; x };
+   |                                 ^^^^^ constants cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/const_let.rs:24:21
    |
 LL | const Z: () = { let mut x = None; x = Some(FakeNeedsDrop); };
-   |                                   ^
+   |                     ^^^^^ constants cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/const_let.rs:28:22
+   |
+LL | const Z2: () = { let mut x; x = None; x = Some(FakeNeedsDrop); };
+   |                      ^^^^^ constants cannot evaluate destructors
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
-For more information about this error, try `rustc --explain E0019`.