about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_error_codes/error_codes/E0493.md12
-rw-r--r--src/librustc_feature/active.rs3
-rw-r--r--src/librustc_interface/passes.rs6
-rw-r--r--src/librustc_middle/mir/mod.rs3
-rw-r--r--src/librustc_middle/query/mod.rs6
-rw-r--r--src/librustc_mir/transform/check_consts/mod.rs1
-rw-r--r--src/librustc_mir/transform/check_consts/ops.rs16
-rw-r--r--src/librustc_mir/transform/check_consts/post_drop_elaboration.rs119
-rw-r--r--src/librustc_mir/transform/check_consts/validation.rs41
-rw-r--r--src/librustc_mir/transform/mod.rs49
-rw-r--r--src/librustc_span/symbol.rs1
-rw-r--r--src/test/ui/consts/control-flow/drop-fail.precise.stderr15
-rw-r--r--src/test/ui/consts/control-flow/drop-fail.rs (renamed from src/test/ui/consts/control-flow/drop-failure.rs)13
-rw-r--r--src/test/ui/consts/control-flow/drop-fail.stock.stderr (renamed from src/test/ui/consts/control-flow/drop-failure.stderr)8
-rw-r--r--src/test/ui/consts/control-flow/drop-pass.rs (renamed from src/test/ui/consts/control-flow/drop-success.rs)2
-rw-r--r--src/test/ui/consts/control-flow/drop-precise.rs20
16 files changed, 264 insertions, 51 deletions
diff --git a/src/librustc_error_codes/error_codes/E0493.md b/src/librustc_error_codes/error_codes/E0493.md
index 90a0cbce623..0dcc3b62b4b 100644
--- a/src/librustc_error_codes/error_codes/E0493.md
+++ b/src/librustc_error_codes/error_codes/E0493.md
@@ -1,5 +1,4 @@
-A type with a `Drop` implementation was destructured when trying to initialize
-a static item.
+A value with a custom `Drop` implementation may be dropped during const-eval.
 
 Erroneous code example:
 
@@ -16,13 +15,14 @@ struct Foo {
     field1: DropType,
 }
 
-static FOO: Foo = Foo { ..Foo { field1: DropType::A } }; // error!
+static FOO: Foo = Foo { field1: (DropType::A, DropType::A).1 }; // error!
 ```
 
 The problem here is that if the given type or one of its fields implements the
-`Drop` trait, this `Drop` implementation cannot be called during the static
-type initialization which might cause a memory leak. To prevent this issue,
-you need to instantiate all the static type's fields by hand.
+`Drop` trait, this `Drop` implementation cannot be called within a const
+context since it may run arbitrary, non-const-checked code. To prevent this
+issue, ensure all values with custom a custom `Drop` implementation escape the
+initializer.
 
 ```
 enum DropType {
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index b4935236b6a..d186f35a12b 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -577,6 +577,9 @@ declare_features! (
     /// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`.
     (active, abi_avr_interrupt, "1.45.0", Some(69664), None),
 
+    /// Be more precise when looking for live drops in a const context.
+    (active, const_precise_live_drops, "1.46.0", Some(73255), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 1a9bf4e1e8f..1ed9bc3f1f5 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -847,7 +847,11 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
 
     sess.time("MIR_effect_checking", || {
         for def_id in tcx.body_owners() {
-            mir::transform::check_unsafety::check_unsafety(tcx, def_id)
+            mir::transform::check_unsafety::check_unsafety(tcx, def_id);
+
+            if tcx.hir().body_const_context(def_id).is_some() {
+                tcx.ensure().mir_drops_elaborated_and_const_checked(def_id);
+            }
         }
     });
 
diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs
index 129f9691ea5..21f5d9e7dd4 100644
--- a/src/librustc_middle/mir/mod.rs
+++ b/src/librustc_middle/mir/mod.rs
@@ -76,7 +76,8 @@ pub enum MirPhase {
     Build = 0,
     Const = 1,
     Validated = 2,
-    Optimized = 3,
+    DropElab = 3,
+    Optimized = 4,
 }
 
 impl MirPhase {
diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs
index be15e6c576f..d758b7737c3 100644
--- a/src/librustc_middle/query/mod.rs
+++ b/src/librustc_middle/query/mod.rs
@@ -190,6 +190,12 @@ rustc_queries! {
             no_hash
         }
 
+        query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> Steal<mir::Body<'tcx>> {
+            storage(ArenaCacheSelector<'tcx>)
+            no_hash
+            desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.to_def_id()) }
+        }
+
         query mir_validated(key: LocalDefId) ->
             (
                 Steal<mir::Body<'tcx>>,
diff --git a/src/librustc_mir/transform/check_consts/mod.rs b/src/librustc_mir/transform/check_consts/mod.rs
index 7c439f80ef6..e4aa88e3c20 100644
--- a/src/librustc_mir/transform/check_consts/mod.rs
+++ b/src/librustc_mir/transform/check_consts/mod.rs
@@ -12,6 +12,7 @@ use rustc_middle::ty::{self, TyCtxt};
 pub use self::qualifs::Qualif;
 
 mod ops;
+pub mod post_drop_elaboration;
 pub mod qualifs;
 mod resolver;
 pub mod validation;
diff --git a/src/librustc_mir/transform/check_consts/ops.rs b/src/librustc_mir/transform/check_consts/ops.rs
index 92bd740e27a..d5059c98c95 100644
--- a/src/librustc_mir/transform/check_consts/ops.rs
+++ b/src/librustc_mir/transform/check_consts/ops.rs
@@ -10,6 +10,22 @@ use rustc_span::{Span, Symbol};
 
 use super::ConstCx;
 
+/// Emits an error if `op` is not allowed in the given const context.
+pub fn non_const<O: NonConstOp>(ccx: &ConstCx<'_, '_>, op: O, span: Span) {
+    debug!("illegal_op: op={:?}", op);
+
+    if op.is_allowed_in_item(ccx) {
+        return;
+    }
+
+    if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
+        ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
+        return;
+    }
+
+    op.emit_error(ccx, span);
+}
+
 /// An operation that is not *always* allowed in a const context.
 pub trait NonConstOp: std::fmt::Debug {
     /// Returns the `Symbol` corresponding to the feature gate that would enable this operation,
diff --git a/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs
new file mode 100644
index 00000000000..226e0e2049e
--- /dev/null
+++ b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs
@@ -0,0 +1,119 @@
+use rustc_hir::def_id::LocalDefId;
+use rustc_middle::mir::visit::Visitor;
+use rustc_middle::mir::{self, BasicBlock, Location};
+use rustc_middle::ty::TyCtxt;
+use rustc_span::Span;
+
+use super::ops;
+use super::qualifs::{NeedsDrop, Qualif};
+use super::validation::Qualifs;
+use super::ConstCx;
+
+/// Returns `true` if we should use the more precise live drop checker that runs after drop
+/// elaboration.
+pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool {
+    tcx.features().const_precise_live_drops
+}
+
+/// Look for live drops in a const context.
+///
+/// This is separate from the rest of the const checking logic because it must run after drop
+/// elaboration.
+pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body<'tcx>) {
+    let const_kind = tcx.hir().body_const_context(def_id);
+    if const_kind.is_none() {
+        return;
+    }
+
+    if !checking_enabled(tcx) {
+        return;
+    }
+
+    let ccx = ConstCx {
+        body,
+        tcx,
+        def_id: def_id.to_def_id(),
+        const_kind,
+        param_env: tcx.param_env(def_id),
+    };
+
+    let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() };
+
+    visitor.visit_body(body);
+}
+
+struct CheckLiveDrops<'mir, 'tcx> {
+    ccx: &'mir ConstCx<'mir, 'tcx>,
+    qualifs: Qualifs<'mir, 'tcx>,
+}
+
+// So we can access `body` and `tcx`.
+impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
+    type Target = ConstCx<'mir, 'tcx>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.ccx
+    }
+}
+
+impl CheckLiveDrops<'mir, 'tcx> {
+    fn check_live_drop(&self, span: Span) {
+        ops::non_const(self.ccx, ops::LiveDrop, span);
+    }
+}
+
+impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
+    fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &mir::BasicBlockData<'tcx>) {
+        trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
+
+        // Ignore drop terminators in cleanup blocks.
+        if block.is_cleanup {
+            return;
+        }
+
+        self.super_basic_block_data(bb, block);
+    }
+
+    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
+        trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
+
+        match &terminator.kind {
+            mir::TerminatorKind::Drop { location: dropped_place, .. } => {
+                let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
+                if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
+                    return;
+                }
+
+                if dropped_place.is_indirect() {
+                    self.check_live_drop(terminator.source_info.span);
+                    return;
+                }
+
+                if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) {
+                    // Use the span where the dropped local was declared for the error.
+                    let span = self.body.local_decls[dropped_place.local].source_info.span;
+                    self.check_live_drop(span);
+                }
+            }
+
+            mir::TerminatorKind::DropAndReplace { .. } => span_bug!(
+                terminator.source_info.span,
+                "`DropAndReplace` should be removed by drop elaboration",
+            ),
+
+            mir::TerminatorKind::Abort
+            | mir::TerminatorKind::Call { .. }
+            | mir::TerminatorKind::Assert { .. }
+            | mir::TerminatorKind::FalseEdge { .. }
+            | mir::TerminatorKind::FalseUnwind { .. }
+            | mir::TerminatorKind::GeneratorDrop
+            | mir::TerminatorKind::Goto { .. }
+            | mir::TerminatorKind::InlineAsm { .. }
+            | mir::TerminatorKind::Resume
+            | mir::TerminatorKind::Return
+            | mir::TerminatorKind::SwitchInt { .. }
+            | mir::TerminatorKind::Unreachable
+            | mir::TerminatorKind::Yield { .. } => {}
+        }
+    }
+}
diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs
index ab87d70da7d..428a74bcdcb 100644
--- a/src/librustc_mir/transform/check_consts/validation.rs
+++ b/src/librustc_mir/transform/check_consts/validation.rs
@@ -40,7 +40,7 @@ pub struct Qualifs<'mir, 'tcx> {
 }
 
 impl Qualifs<'mir, 'tcx> {
-    fn indirectly_mutable(
+    pub fn indirectly_mutable(
         &mut self,
         ccx: &'mir ConstCx<'mir, 'tcx>,
         local: Local,
@@ -68,7 +68,7 @@ impl Qualifs<'mir, 'tcx> {
     /// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
     ///
     /// Only updates the cursor if absolutely necessary
-    fn needs_drop(
+    pub fn needs_drop(
         &mut self,
         ccx: &'mir ConstCx<'mir, 'tcx>,
         local: Local,
@@ -95,7 +95,7 @@ impl Qualifs<'mir, 'tcx> {
     /// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
     ///
     /// Only updates the cursor if absolutely necessary.
-    fn has_mut_interior(
+    pub fn has_mut_interior(
         &mut self,
         ccx: &'mir ConstCx<'mir, 'tcx>,
         local: Local,
@@ -232,30 +232,15 @@ impl Validator<'mir, 'tcx> {
         self.qualifs.in_return_place(self.ccx)
     }
 
-    /// Emits an error at the given `span` if an expression cannot be evaluated in the current
-    /// context.
-    pub fn check_op_spanned<O>(&mut self, op: O, span: Span)
-    where
-        O: NonConstOp,
-    {
-        debug!("check_op: op={:?}", op);
-
-        if op.is_allowed_in_item(self) {
-            return;
-        }
-
-        if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
-            self.tcx.sess.miri_unleashed_feature(span, O::feature_gate());
-            return;
-        }
-
-        op.emit_error(self, span);
-    }
-
     /// Emits an error if an expression cannot be evaluated in the current context.
     pub fn check_op(&mut self, op: impl NonConstOp) {
-        let span = self.span;
-        self.check_op_spanned(op, span)
+        ops::non_const(self.ccx, op, self.span);
+    }
+
+    /// Emits an error at the given `span` if an expression cannot be evaluated in the current
+    /// context.
+    pub fn check_op_spanned(&mut self, op: impl NonConstOp, span: Span) {
+        ops::non_const(self.ccx, op, span);
     }
 
     fn check_static(&mut self, def_id: DefId, span: Span) {
@@ -577,6 +562,12 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
             // projections that cannot be `NeedsDrop`.
             TerminatorKind::Drop { location: dropped_place, .. }
             | TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
+                // If we are checking live drops after drop-elaboration, don't emit duplicate
+                // errors here.
+                if super::post_drop_elaboration::checking_enabled(self.tcx) {
+                    return;
+                }
+
                 let mut err_span = self.span;
 
                 // Check to see if the type of this place can ever have a drop impl. If not, this
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 26725a2ac02..4240b528a61 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -49,6 +49,7 @@ pub(crate) fn provide(providers: &mut Providers<'_>) {
         mir_const,
         mir_const_qualif,
         mir_validated,
+        mir_drops_elaborated_and_const_checked,
         optimized_mir,
         is_mir_available,
         promoted_mir,
@@ -294,12 +295,31 @@ fn mir_validated(
     (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
 }
 
-fn run_optimization_passes<'tcx>(
+fn mir_drops_elaborated_and_const_checked<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+) -> Steal<Body<'tcx>> {
+    // (Mir-)Borrowck uses `mir_validated`, so we have to force it to
+    // execute before we can steal.
+    tcx.ensure().mir_borrowck(def_id);
+
+    let (body, _) = tcx.mir_validated(def_id);
+    let mut body = body.steal();
+
+    run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, None);
+    check_consts::post_drop_elaboration::check_live_drops(tcx, def_id, &body);
+    tcx.alloc_steal_mir(body)
+}
+
+/// After this series of passes, no lifetime analysis based on borrowing can be done.
+fn run_post_borrowck_cleanup_passes<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &mut Body<'tcx>,
     def_id: LocalDefId,
     promoted: Option<Promoted>,
 ) {
+    debug!("post_borrowck_cleanup({:?})", def_id);
+
     let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
         // Remove all things only needed by analysis
         &no_landing_pads::NoLandingPads::new(tcx),
@@ -318,9 +338,24 @@ fn run_optimization_passes<'tcx>(
         // but before optimizations begin.
         &add_retag::AddRetag,
         &simplify::SimplifyCfg::new("elaborate-drops"),
-        // No lifetime analysis based on borrowing can be done from here on out.
     ];
 
+    run_passes(
+        tcx,
+        body,
+        InstanceDef::Item(def_id.to_def_id()),
+        promoted,
+        MirPhase::DropElab,
+        &[post_borrowck_cleanup],
+    );
+}
+
+fn run_optimization_passes<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &mut Body<'tcx>,
+    def_id: LocalDefId,
+    promoted: Option<Promoted>,
+) {
     let optimizations: &[&dyn MirPass<'tcx>] = &[
         &unreachable_prop::UnreachablePropagation,
         &uninhabited_enum_branching::UninhabitedEnumBranching,
@@ -368,6 +403,7 @@ fn run_optimization_passes<'tcx>(
 
     let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level;
 
+    #[rustfmt::skip]
     run_passes(
         tcx,
         body,
@@ -375,7 +411,6 @@ fn run_optimization_passes<'tcx>(
         promoted,
         MirPhase::Optimized,
         &[
-            post_borrowck_cleanup,
             if mir_opt_level > 0 { optimizations } else { no_optimizations },
             pre_codegen_cleanup,
         ],
@@ -393,12 +428,7 @@ fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
 
     let def_id = def_id.expect_local();
 
-    // (Mir-)Borrowck uses `mir_validated`, so we have to force it to
-    // execute before we can steal.
-    tcx.ensure().mir_borrowck(def_id);
-
-    let (body, _) = tcx.mir_validated(def_id);
-    let mut body = body.steal();
+    let mut body = tcx.mir_drops_elaborated_and_const_checked(def_id).steal();
     run_optimization_passes(tcx, &mut body, def_id, None);
 
     debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
@@ -418,6 +448,7 @@ fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> IndexVec<Promoted, Body<'_>>
     let mut promoted = promoted.steal();
 
     for (p, mut body) in promoted.iter_enumerated_mut() {
+        run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, Some(p));
         run_optimization_passes(tcx, &mut body, def_id, Some(p));
     }
 
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index fea5880f01e..fdeb58b7b7a 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -227,6 +227,7 @@ symbols! {
         const_loop,
         const_mut_refs,
         const_panic,
+        const_precise_live_drops,
         const_raw_ptr_deref,
         const_raw_ptr_to_usize_cast,
         const_transmute,
diff --git a/src/test/ui/consts/control-flow/drop-fail.precise.stderr b/src/test/ui/consts/control-flow/drop-fail.precise.stderr
new file mode 100644
index 00000000000..b4b6be8a1e5
--- /dev/null
+++ b/src/test/ui/consts/control-flow/drop-fail.precise.stderr
@@ -0,0 +1,15 @@
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/drop-fail.rs:10:9
+   |
+LL |     let x = Some(Vec::new());
+   |         ^ constants cannot evaluate destructors
+
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/drop-fail.rs:41:9
+   |
+LL |     let mut tmp = None;
+   |         ^^^^^^^ constants cannot evaluate destructors
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0493`.
diff --git a/src/test/ui/consts/control-flow/drop-failure.rs b/src/test/ui/consts/control-flow/drop-fail.rs
index 9da5546976c..7bd36726cea 100644
--- a/src/test/ui/consts/control-flow/drop-failure.rs
+++ b/src/test/ui/consts/control-flow/drop-fail.rs
@@ -1,11 +1,14 @@
+// revisions: stock precise
+
 #![feature(const_if_match)]
 #![feature(const_loop)]
+#![cfg_attr(precise, feature(const_precise_live_drops))]
 
-// `x` is *not* always moved into the final value may be dropped inside the initializer.
+// `x` is *not* always moved into the final value and may be dropped inside the initializer.
 const _: Option<Vec<i32>> = {
     let y: Option<Vec<i32>> = None;
     let x = Some(Vec::new());
-    //~^ ERROR destructors cannot be evaluated at compile-time
+    //[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time
 
     if true {
         x
@@ -18,7 +21,7 @@ const _: Option<Vec<i32>> = {
 // existing analysis.
 const _: Vec<i32> = {
     let vec_tuple = (Vec::new(),);
-    //~^ ERROR destructors cannot be evaluated at compile-time
+    //[stock]~^ ERROR destructors cannot be evaluated at compile-time
 
     vec_tuple.0
 };
@@ -26,7 +29,7 @@ const _: Vec<i32> = {
 // This applies to single-field enum variants as well.
 const _: Vec<i32> = {
     let x: Result<_, Vec<i32>> = Ok(Vec::new());
-    //~^ ERROR destructors cannot be evaluated at compile-time
+    //[stock]~^ ERROR destructors cannot be evaluated at compile-time
 
     match x {
         Ok(x) | Err(x) => x,
@@ -36,7 +39,7 @@ const _: Vec<i32> = {
 const _: Option<Vec<i32>> = {
     let mut some = Some(Vec::new());
     let mut tmp = None;
-    //~^ ERROR destructors cannot be evaluated at compile-time
+    //[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time
 
     let mut i = 0;
     while i < 10 {
diff --git a/src/test/ui/consts/control-flow/drop-failure.stderr b/src/test/ui/consts/control-flow/drop-fail.stock.stderr
index 3eec3a929a0..77cded5c438 100644
--- a/src/test/ui/consts/control-flow/drop-failure.stderr
+++ b/src/test/ui/consts/control-flow/drop-fail.stock.stderr
@@ -1,23 +1,23 @@
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/drop-failure.rs:7:9
+  --> $DIR/drop-fail.rs:10:9
    |
 LL |     let x = Some(Vec::new());
    |         ^ constants cannot evaluate destructors
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/drop-failure.rs:20:9
+  --> $DIR/drop-fail.rs:23:9
    |
 LL |     let vec_tuple = (Vec::new(),);
    |         ^^^^^^^^^ constants cannot evaluate destructors
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/drop-failure.rs:28:9
+  --> $DIR/drop-fail.rs:31:9
    |
 LL |     let x: Result<_, Vec<i32>> = Ok(Vec::new());
    |         ^ constants cannot evaluate destructors
 
 error[E0493]: destructors cannot be evaluated at compile-time
-  --> $DIR/drop-failure.rs:38:9
+  --> $DIR/drop-fail.rs:41:9
    |
 LL |     let mut tmp = None;
    |         ^^^^^^^ constants cannot evaluate destructors
diff --git a/src/test/ui/consts/control-flow/drop-success.rs b/src/test/ui/consts/control-flow/drop-pass.rs
index 185d6b63996..b0afd76c4e6 100644
--- a/src/test/ui/consts/control-flow/drop-success.rs
+++ b/src/test/ui/consts/control-flow/drop-pass.rs
@@ -1,7 +1,9 @@
 // run-pass
+// revisions: stock precise
 
 #![feature(const_if_match)]
 #![feature(const_loop)]
+#![cfg_attr(precise, feature(const_precise_live_drops))]
 
 // `x` is always moved into the final value and is not dropped inside the initializer.
 const _: Option<Vec<i32>> = {
diff --git a/src/test/ui/consts/control-flow/drop-precise.rs b/src/test/ui/consts/control-flow/drop-precise.rs
new file mode 100644
index 00000000000..95df76d9905
--- /dev/null
+++ b/src/test/ui/consts/control-flow/drop-precise.rs
@@ -0,0 +1,20 @@
+// run-pass
+// gate-test-const_precise_live_drops
+
+#![feature(const_if_match)]
+#![feature(const_loop)]
+#![feature(const_precise_live_drops)]
+
+const _: Vec<i32> = {
+    let vec_tuple = (Vec::new(),);
+    vec_tuple.0
+};
+
+const _: Vec<i32> = {
+    let x: Result<_, Vec<i32>> = Ok(Vec::new());
+    match x {
+        Ok(x) | Err(x) => x,
+    }
+};
+
+fn main() {}