]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #66385 - ecstatic-morse:check-only-pass2, r=eddyb
authorbors <bors@rust-lang.org>
Sun, 17 Nov 2019 18:38:15 +0000 (18:38 +0000)
committerbors <bors@rust-lang.org>
Sun, 17 Nov 2019 18:38:15 +0000 (18:38 +0000)
Make dataflow-based const qualification the canonical one

For over a month, dataflow-based const qualification has been running in parallel with `qualify_consts` to check the bodies of `const` and `static`s. This PR removes the old qualification pass completely in favor of the dataflow-based one.

**edit:**
This PR also stops checking `QUALIF_ERROR_BIT` during promotion. This check appears to no longer serve a purpose now that the CTFE engine is more robust.

As a side-effect, this resolves #66167.

r? @eddyb

20 files changed:
src/librustc/mir/mod.rs
src/librustc/query/mod.rs
src/librustc/session/config.rs
src/librustc_metadata/rmeta/decoder.rs
src/librustc_metadata/rmeta/encoder.rs
src/librustc_metadata/rmeta/mod.rs
src/librustc_mir/lib.rs
src/librustc_mir/transform/check_consts/mod.rs
src/librustc_mir/transform/check_consts/ops.rs
src/librustc_mir/transform/check_consts/qualifs.rs
src/librustc_mir/transform/check_consts/validation.rs
src/librustc_mir/transform/mod.rs
src/librustc_mir/transform/promote_consts.rs
src/librustc_mir/transform/qualify_consts.rs [deleted file]
src/test/compile-fail/consts/const-fn-error.rs
src/test/compile-fail/issue-52443.rs
src/test/ui/consts/const-eval/issue-65394.rs
src/test/ui/consts/const-eval/issue-65394.stderr
src/test/ui/consts/miri_unleashed/enum_discriminants.rs
src/test/ui/consts/miri_unleashed/enum_discriminants.stderr [deleted file]

index dd0d828fdcd8b13f04eeb2a61560e7ff644af5fd..5997e4367bf29397cf439b05e4758f3e23d3cd05 100644 (file)
@@ -2763,6 +2763,17 @@ pub struct BorrowCheckResult<'tcx> {
     pub used_mut_upvars: SmallVec<[Field; 8]>,
 }
 
+/// The result of the `mir_const_qualif` query.
+///
+/// Each field corresponds to an implementer of the `Qualif` trait in
+/// `librustc_mir/transform/check_consts/qualifs.rs`. See that file for more information on each
+/// `Qualif`.
+#[derive(Clone, Copy, Debug, Default, RustcEncodable, RustcDecodable, HashStable)]
+pub struct ConstQualifs {
+    pub has_mut_interior: bool,
+    pub needs_drop: bool,
+}
+
 /// After we borrow check a closure, we are left with various
 /// requirements that we have inferred between the free regions that
 /// appear in the closure's signature or on its field types. These
index 9bd2a933c1c694095ac12f276f3ba67c59707761..e07726bfa2aa12ca689a9c0afdddc170c894f341 100644 (file)
@@ -91,9 +91,9 @@
         }
 
         /// Maps DefId's that have an associated `mir::Body` to the result
-        /// of the MIR qualify_consts pass. The actual meaning of
-        /// the value isn't known except to the pass itself.
-        query mir_const_qualif(key: DefId) -> u8 {
+        /// of the MIR const-checking pass. This is the set of qualifs in
+        /// the final value of a `const`.
+        query mir_const_qualif(key: DefId) -> mir::ConstQualifs {
             desc { |tcx| "const checking `{}`", tcx.def_path_str(key) }
             cache_on_disk_if { key.is_local() }
         }
index 4a14960aa69bd8f340083adeb07ee77e5b7a8af0..4474b008c79492179a03f9aba2e36a05f095b6ea 100644 (file)
@@ -1373,8 +1373,6 @@ fn parse_symbol_mangling_version(
         "describes how to render the `rendered` field of json diagnostics"),
     unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED],
         "take the breaks off const evaluation. NOTE: this is unsound"),
-    suppress_const_validation_back_compat_ice: bool = (false, parse_bool, [TRACKED],
-        "silence ICE triggered when the new const validator disagrees with the old"),
     osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
         "pass `-install_name @rpath/...` to the macOS linker"),
     sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
index 7cebf2512d64521286ff4316d63f1fdcee854c57..fc0a0010240ad9b165d0598956dfb0c08e3956a8 100644 (file)
@@ -952,12 +952,12 @@ fn get_promoted_mir(
             .decode((self, tcx))
     }
 
-    fn mir_const_qualif(&self, id: DefIndex) -> u8 {
+    fn mir_const_qualif(&self, id: DefIndex) -> mir::ConstQualifs {
         match self.kind(id) {
             EntryKind::Const(qualif, _) |
             EntryKind::AssocConst(AssocContainer::ImplDefault, qualif, _) |
             EntryKind::AssocConst(AssocContainer::ImplFinal, qualif, _) => {
-                qualif.mir
+                qualif
             }
             _ => bug!(),
         }
index 26055d329bcbd19736321a6604a474600b172a67..483915f654dddd59810e95b9a127d8d78133e05e 100644 (file)
@@ -875,7 +875,11 @@ fn encode_info_for_trait_item(&mut self, def_id: DefId) {
                     hir::print::to_string(self.tcx.hir(), |s| s.print_trait_item(ast_item));
                 let rendered_const = self.lazy(RenderedConst(rendered));
 
-                EntryKind::AssocConst(container, ConstQualif { mir: 0 }, rendered_const)
+                EntryKind::AssocConst(
+                    container,
+                    Default::default(),
+                    rendered_const,
+                )
             }
             ty::AssocKind::Method => {
                 let fn_data = if let hir::TraitItemKind::Method(m_sig, m) = &ast_item.kind {
@@ -955,10 +959,11 @@ fn encode_info_for_impl_item(&mut self, def_id: DefId) {
         record!(self.per_def.kind[def_id] <- match impl_item.kind {
             ty::AssocKind::Const => {
                 if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
-                    let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
+                    let qualifs = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
 
-                    EntryKind::AssocConst(container,
-                        ConstQualif { mir },
+                    EntryKind::AssocConst(
+                        container,
+                        qualifs,
                         self.encode_rendered_const_for_body(body_id))
                 } else {
                     bug!()
@@ -1089,9 +1094,9 @@ fn encode_info_for_item(&mut self, def_id: DefId, item: &'tcx hir::Item) {
             hir::ItemKind::Static(_, hir::Mutability::Mutable, _) => EntryKind::MutStatic,
             hir::ItemKind::Static(_, hir::Mutability::Immutable, _) => EntryKind::ImmStatic,
             hir::ItemKind::Const(_, body_id) => {
-                let mir = self.tcx.at(item.span).mir_const_qualif(def_id);
+                let qualifs = self.tcx.at(item.span).mir_const_qualif(def_id);
                 EntryKind::Const(
-                    ConstQualif { mir },
+                    qualifs,
                     self.encode_rendered_const_for_body(body_id)
                 )
             }
@@ -1368,9 +1373,9 @@ fn encode_info_for_anon_const(&mut self, def_id: DefId) {
         let id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
         let body_id = self.tcx.hir().body_owned_by(id);
         let const_data = self.encode_rendered_const_for_body(body_id);
-        let mir = self.tcx.mir_const_qualif(def_id);
+        let qualifs = self.tcx.mir_const_qualif(def_id);
 
-        record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data));
+        record!(self.per_def.kind[def_id] <- EntryKind::Const(qualifs, const_data));
         record!(self.per_def.visibility[def_id] <- ty::Visibility::Public);
         record!(self.per_def.span[def_id] <- self.tcx.def_span(def_id));
         self.encode_item_type(def_id);
index 990a3d984b225ef8776c23788f89eb682985ce7d..850ee5afbc808479fb8685d2b1d42ae0e9826c43 100644 (file)
@@ -260,7 +260,7 @@ macro_rules! Lazy {
 
 #[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
 enum EntryKind<'tcx> {
-    Const(ConstQualif, Lazy<RenderedConst>),
+    Const(mir::ConstQualifs, Lazy<RenderedConst>),
     ImmStatic,
     MutStatic,
     ForeignImmStatic,
@@ -288,16 +288,10 @@ enum EntryKind<'tcx> {
     Method(Lazy<MethodData>),
     AssocType(AssocContainer),
     AssocOpaqueTy(AssocContainer),
-    AssocConst(AssocContainer, ConstQualif, Lazy<RenderedConst>),
+    AssocConst(AssocContainer, mir::ConstQualifs, Lazy<RenderedConst>),
     TraitAlias,
 }
 
-/// Additional data for EntryKind::Const and EntryKind::AssocConst
-#[derive(Clone, Copy, RustcEncodable, RustcDecodable)]
-struct ConstQualif {
-    mir: u8,
-}
-
 /// Contains a constant which has been rendered to a String.
 /// Used by rustdoc.
 #[derive(RustcEncodable, RustcDecodable)]
index 6408c896e93015fd26d7d4049fbb0aa6782739f1..6d19cd63bc32ea1ef24a9bd83d0b949d75f7ed22 100644 (file)
@@ -32,7 +32,6 @@
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate rustc;
-#[macro_use] extern crate rustc_data_structures;
 #[macro_use] extern crate syntax;
 
 mod borrow_check;
index a5b711e75a603e01535d1bde8c7d74bfe312be10..4d00aaf7abc212df6c1c571a7ebf9b986a61253d 100644 (file)
@@ -95,16 +95,6 @@ pub fn is_static(self) -> bool {
             ConstKind::ConstFn | ConstKind::Const => false,
         }
     }
-
-    /// Returns `true` if the value returned by this item must be `Sync`.
-    ///
-    /// This returns false for `StaticMut` since all accesses to one are `unsafe` anyway.
-    pub fn requires_sync(self) -> bool {
-        match self {
-            ConstKind::Static => true,
-            ConstKind::ConstFn | ConstKind::Const |  ConstKind::StaticMut => false,
-        }
-    }
 }
 
 impl fmt::Display for ConstKind {
index 303c3984f7c0f66dd6e9bc5ee17b2cdce53f723b..80f2925193a8140a87785e1b8b0a273d6b0ab347 100644 (file)
@@ -138,7 +138,15 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
 
 #[derive(Debug)]
 pub struct IfOrMatch;
-impl NonConstOp for IfOrMatch {}
+impl NonConstOp for IfOrMatch {
+    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
+        // This should be caught by the HIR const-checker.
+        item.tcx.sess.delay_span_bug(
+            span,
+            "complex control flow is forbidden in a const context",
+        );
+    }
+}
 
 #[derive(Debug)]
 pub struct LiveDrop;
@@ -154,7 +162,15 @@ fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
 
 #[derive(Debug)]
 pub struct Loop;
-impl NonConstOp for Loop {}
+impl NonConstOp for Loop {
+    fn emit_error(&self, item: &Item<'_, '_>, span: Span) {
+        // This should be caught by the HIR const-checker.
+        item.tcx.sess.delay_span_bug(
+            span,
+            "complex control flow is forbidden in a const context",
+        );
+    }
+}
 
 #[derive(Debug)]
 pub struct MutBorrow(pub BorrowKind);
index 595ef2aad49d912d602db637e8dc27530ff9a146..aad14299c1d94105532ea4004cf4d0e920377a9a 100644 (file)
@@ -6,12 +6,10 @@
 
 use super::{ConstKind, Item as ConstCx};
 
-#[derive(Clone, Copy)]
-pub struct QualifSet(u8);
-
-impl QualifSet {
-    fn contains<Q: ?Sized + Qualif>(self) -> bool {
-        self.0 & (1 << Q::IDX) != 0
+pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
+    ConstQualifs {
+        has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
+        needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
     }
 }
 
@@ -22,14 +20,14 @@ fn contains<Q: ?Sized + Qualif>(self) -> bool {
 ///
 /// The default implementations proceed structurally.
 pub trait Qualif {
-    const IDX: usize;
-
     /// The name of the file used to debug the dataflow analysis that computes this qualif.
     const ANALYSIS_NAME: &'static str;
 
     /// Whether this `Qualif` is cleared when a local is moved from.
     const IS_CLEARED_ON_MOVE: bool = false;
 
+    fn in_qualifs(qualifs: &ConstQualifs) -> bool;
+
     /// Return the qualification that is (conservatively) correct for any value
     /// of the type.
     fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
@@ -122,9 +120,8 @@ fn in_operand(
                     if cx.tcx.trait_of_item(def_id).is_some() {
                         Self::in_any_value_of_ty(cx, constant.literal.ty)
                     } else {
-                        let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
-
-                        let qualif = QualifSet(bits).contains::<Self>();
+                        let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
+                        let qualif = Self::in_qualifs(&qualifs);
 
                         // Just in case the type is more specific than
                         // the definition, e.g., impl associated const
@@ -210,9 +207,12 @@ fn in_call(
 pub struct HasMutInterior;
 
 impl Qualif for HasMutInterior {
-    const IDX: usize = 0;
     const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
 
+    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
+        qualifs.has_mut_interior
+    }
+
     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
         !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
     }
@@ -275,10 +275,13 @@ fn in_rvalue(
 pub struct NeedsDrop;
 
 impl Qualif for NeedsDrop {
-    const IDX: usize = 1;
     const ANALYSIS_NAME: &'static str = "flow_needs_drop";
     const IS_CLEARED_ON_MOVE: bool = true;
 
+    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
+        qualifs.needs_drop
+    }
+
     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
         ty.needs_drop(cx.tcx, cx.param_env)
     }
index 74b22d8e14366af24025dabaf7fd15ac9fdad5cc..21e7c9ce565f0103d99903f5b85f0ca1775c8e37 100644 (file)
@@ -1,21 +1,25 @@
 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
 
+use rustc::hir::HirId;
+use rustc::middle::lang_items;
 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
 use rustc::mir::*;
+use rustc::traits::{self, TraitEngine};
 use rustc::ty::cast::CastTy;
-use rustc::ty;
+use rustc::ty::{self, TyCtxt};
 use rustc_index::bit_set::BitSet;
 use rustc_target::spec::abi::Abi;
+use rustc_error_codes::*;
 use syntax::symbol::sym;
 use syntax_pos::Span;
 
-use std::fmt;
+use std::borrow::Cow;
 use std::ops::Deref;
 
 use crate::dataflow::{self as old_dataflow, generic as dataflow};
 use self::old_dataflow::IndirectlyMutableLocals;
 use super::ops::{self, NonConstOp};
-use super::qualifs::{HasMutInterior, NeedsDrop};
+use super::qualifs::{self, HasMutInterior, NeedsDrop};
 use super::resolver::FlowSensitiveAnalysis;
 use super::{ConstKind, Item, Qualif, is_lang_panic_fn};
 
@@ -85,6 +89,19 @@ fn needs_drop_lazy_seek(&mut self, local: Local, location: Location) -> bool {
             || self.indirectly_mutable(local, location)
     }
 
+    /// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
+    ///
+    /// Only updates the cursor if absolutely necessary.
+    fn has_mut_interior_lazy_seek(&mut self, local: Local, location: Location) -> bool {
+        if !self.has_mut_interior.in_any_value_of_ty.contains(local) {
+            return false;
+        }
+
+        self.has_mut_interior.cursor.seek_before(location);
+        self.has_mut_interior.cursor.get().contains(local)
+            || self.indirectly_mutable(local, location)
+    }
+
     /// Returns `true` if `local` is `HasMutInterior`, but requires the `has_mut_interior` and
     /// `indirectly_mutable` cursors to be updated beforehand.
     fn has_mut_interior_eager_seek(&self, local: Local) -> bool {
@@ -95,6 +112,35 @@ fn has_mut_interior_eager_seek(&self, local: Local) -> bool {
         self.has_mut_interior.cursor.get().contains(local)
             || self.indirectly_mutable.get().contains(local)
     }
+
+    fn in_return_place(&mut self, item: &Item<'_, 'tcx>) -> ConstQualifs {
+        // Find the `Return` terminator if one exists.
+        //
+        // If no `Return` terminator exists, this MIR is divergent. Just return the conservative
+        // qualifs for the return type.
+        let return_block = item.body
+            .basic_blocks()
+            .iter_enumerated()
+            .find(|(_, block)| {
+                match block.terminator().kind {
+                    TerminatorKind::Return => true,
+                    _ => false,
+                }
+            })
+            .map(|(bb, _)| bb);
+
+        let return_block = match return_block {
+            None => return qualifs::in_any_value_of_ty(item, item.body.return_ty()),
+            Some(bb) => bb,
+        };
+
+        let return_loc = item.body.terminator_loc(return_block);
+
+        ConstQualifs {
+            needs_drop: self.needs_drop_lazy_seek(RETURN_PLACE, return_loc),
+            has_mut_interior: self.has_mut_interior_lazy_seek(RETURN_PLACE, return_loc),
+        }
+    }
 }
 
 pub struct Validator<'a, 'mir, 'tcx> {
@@ -114,11 +160,6 @@ pub struct Validator<'a, 'mir, 'tcx> {
     /// this set is empty. Note that if we start removing locals from
     /// `derived_from_illegal_borrow`, just checking at the end won't be enough.
     derived_from_illegal_borrow: BitSet<Local>,
-
-    errors: Vec<(Span, String)>,
-
-    /// Whether to actually emit errors or just store them in `errors`.
-    pub(crate) suppress_errors: bool,
 }
 
 impl Deref for Validator<'_, 'mir, 'tcx> {
@@ -172,21 +213,55 @@ pub fn new(
             span: item.body.span,
             item,
             qualifs,
-            errors: vec![],
             derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()),
-            suppress_errors: false,
         }
     }
 
-    pub fn take_errors(&mut self) -> Vec<(Span, String)> {
-        std::mem::replace(&mut self.errors, vec![])
+    pub fn check_body(&mut self) {
+        let Item { tcx, body, def_id, const_kind, ..  } = *self.item;
+
+        let use_min_const_fn_checks =
+            tcx.is_min_const_fn(def_id)
+            && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
+
+        if use_min_const_fn_checks {
+            // Enforce `min_const_fn` for stable `const fn`s.
+            use crate::transform::qualify_min_const_fn::is_min_const_fn;
+            if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
+                error_min_const_fn_violation(tcx, span, err);
+                return;
+            }
+        }
+
+        check_short_circuiting_in_const_local(self.item);
+
+        if body.is_cfg_cyclic() {
+            // We can't provide a good span for the error here, but this should be caught by the
+            // HIR const-checker anyways.
+            self.check_op_spanned(ops::Loop, body.span);
+        }
+
+        self.visit_body(body);
+
+        // Ensure that the end result is `Sync` in a non-thread local `static`.
+        let should_check_for_sync = const_kind == Some(ConstKind::Static)
+            && !tcx.has_attr(def_id, sym::thread_local);
+
+        if should_check_for_sync {
+            let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+            check_return_ty_is_sync(tcx, body, hir_id);
+        }
+    }
+
+    pub fn qualifs_in_return_place(&mut self) -> ConstQualifs {
+        self.qualifs.in_return_place(self.item)
     }
 
     /// Emits an error at the given `span` if an expression cannot be evaluated in the current
     /// context. Returns `Forbidden` if an error was emitted.
     pub fn check_op_spanned<O>(&mut self, op: O, span: Span) -> CheckOpResult
     where
-        O: NonConstOp + fmt::Debug
+        O: NonConstOp
     {
         trace!("check_op: op={:?}", op);
 
@@ -204,22 +279,37 @@ pub fn check_op_spanned<O>(&mut self, op: O, span: Span) -> CheckOpResult
             return CheckOpResult::Unleashed;
         }
 
-        if !self.suppress_errors {
-            op.emit_error(self, span);
-        }
-
-        self.errors.push((span, format!("{:?}", op)));
+        op.emit_error(self, span);
         CheckOpResult::Forbidden
     }
 
     /// Emits an error if an expression cannot be evaluated in the current context.
-    pub fn check_op(&mut self, op: impl NonConstOp + fmt::Debug) -> CheckOpResult {
+    pub fn check_op(&mut self, op: impl NonConstOp) -> CheckOpResult {
         let span = self.span;
         self.check_op_spanned(op, span)
     }
 }
 
 impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
+    fn visit_basic_block_data(
+        &mut self,
+        bb: BasicBlock,
+        block: &BasicBlockData<'tcx>,
+    ) {
+        trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
+
+        // Just as the old checker did, we skip const-checking basic blocks on the unwind path.
+        // These blocks often drop locals that would otherwise be returned from the function.
+        //
+        // FIXME: This shouldn't be unsound since a panic at compile time will cause a compiler
+        // error anyway, but maybe we should do more here?
+        if block.is_cleanup {
+            return;
+        }
+
+        self.super_basic_block_data(bb, block);
+    }
+
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
 
@@ -451,14 +541,7 @@ fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
                 self.super_statement(statement, location);
             }
             StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
-                // FIXME: make this the `emit_error` impl of `ops::IfOrMatch` once the const
-                // checker is no longer run in compatability mode.
-                if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
-                    self.tcx.sess.delay_span_bug(
-                        self.span,
-                        "complex control flow is forbidden in a const context",
-                    );
-                }
+                self.check_op(ops::IfOrMatch);
             }
             // FIXME(eddyb) should these really do nothing?
             StatementKind::FakeRead(..) |
@@ -564,3 +647,58 @@ fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Locat
         }
     }
 }
+
+fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
+    struct_span_err!(tcx.sess, span, E0723, "{}", msg)
+        .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
+        .help("add `#![feature(const_fn)]` to the crate attributes to enable")
+        .emit();
+}
+
+fn check_short_circuiting_in_const_local(item: &Item<'_, 'tcx>) {
+    let body = item.body;
+
+    if body.control_flow_destroyed.is_empty() {
+        return;
+    }
+
+    let mut locals = body.vars_iter();
+    if let Some(local) = locals.next() {
+        let span = body.local_decls[local].source_info.span;
+        let mut error = item.tcx.sess.struct_span_err(
+            span,
+            &format!(
+                "new features like let bindings are not permitted in {}s \
+                which also use short circuiting operators",
+                item.const_kind(),
+            ),
+        );
+        for (span, kind) in body.control_flow_destroyed.iter() {
+            error.span_note(
+                *span,
+                &format!("use of {} here does not actually short circuit due to \
+                the const evaluator presently not being able to do control flow. \
+                See https://github.com/rust-lang/rust/issues/49146 for more \
+                information.", kind),
+            );
+        }
+        for local in locals {
+            let span = body.local_decls[local].source_info.span;
+            error.span_note(span, "more locals defined here");
+        }
+        error.emit();
+    }
+}
+
+fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) {
+    let ty = body.return_ty();
+    tcx.infer_ctxt().enter(|infcx| {
+        let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
+        let mut fulfillment_cx = traits::FulfillmentContext::new();
+        let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
+        fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
+        if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
+            infcx.report_fulfillment_errors(&err, None, false);
+        }
+    });
+}
index 02ed12eda67a2ad4d071e5fcd09d2931bb1aafec..897e37858a68edec0f07345321c5bc75a6ca1a03 100644 (file)
@@ -1,8 +1,8 @@
 use crate::{build, shim};
 use rustc_index::vec::IndexVec;
 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use rustc::mir::{Body, MirPhase, Promoted};
-use rustc::ty::{TyCtxt, InstanceDef};
+use rustc::mir::{Body, MirPhase, Promoted, ConstQualifs};
+use rustc::ty::{TyCtxt, InstanceDef, TypeFoldable};
 use rustc::ty::query::Providers;
 use rustc::ty::steal::Steal;
 use rustc::hir;
@@ -25,7 +25,6 @@
 pub mod elaborate_drops;
 pub mod add_call_guards;
 pub mod promote_consts;
-pub mod qualify_consts;
 pub mod qualify_min_const_fn;
 pub mod remove_noop_landing_pads;
 pub mod dump_mir;
 pub mod uninhabited_enum_branching;
 
 pub(crate) fn provide(providers: &mut Providers<'_>) {
-    self::qualify_consts::provide(providers);
     self::check_unsafety::provide(providers);
     *providers = Providers {
         mir_keys,
         mir_built,
         mir_const,
+        mir_const_qualif,
         mir_validated,
         optimized_mir,
         is_mir_available,
@@ -185,6 +184,41 @@ pub fn run_passes(
     body.phase = mir_phase;
 }
 
+fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
+    let const_kind = check_consts::ConstKind::for_item(tcx, def_id);
+
+    // No need to const-check a non-const `fn`.
+    if const_kind.is_none() {
+        return Default::default();
+    }
+
+    // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
+    // cannot yet be stolen), because `mir_validated()`, which steals
+    // from `mir_const(), forces this query to execute before
+    // performing the steal.
+    let body = &tcx.mir_const(def_id).borrow();
+
+    if body.return_ty().references_error() {
+        tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
+        return Default::default();
+    }
+
+    let item = check_consts::Item {
+        body,
+        tcx,
+        def_id,
+        const_kind,
+        param_env: tcx.param_env(def_id),
+    };
+
+    let mut validator = check_consts::validation::Validator::new(&item);
+    validator.check_body();
+
+    // We return the qualifs in the return place for every MIR body, even though it is only used
+    // when deciding to promote a reference to a `const` for now.
+    validator.qualifs_in_return_place().into()
+}
+
 fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<Body<'_>> {
     // Unsafety check uses the raw mir, so make sure it is run
     let _ = tcx.unsafety_check_result(def_id);
@@ -203,18 +237,14 @@ fn mir_validated(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
 ) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
-    let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
-    if let hir::BodyOwnerKind::Const = tcx.hir().body_owner_kind(hir_id) {
-        // Ensure that we compute the `mir_const_qualif` for constants at
-        // this point, before we steal the mir-const result.
-        let _ = tcx.mir_const_qualif(def_id);
-    }
+    // Ensure that we compute the `mir_const_qualif` for constants at
+    // this point, before we steal the mir-const result.
+    let _ = tcx.mir_const_qualif(def_id);
 
     let mut body = tcx.mir_const(def_id).steal();
     let promote_pass = promote_consts::PromoteTemps::default();
     run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
         // What we need to run borrowck etc.
-        &qualify_consts::QualifyAndPromoteConstants::default(),
         &promote_pass,
         &simplify::SimplifyCfg::new("qualify-consts"),
     ]);
index b4da437279db13279325a63f365ced78a993b088..c79d382a37480a9c6a5a1787bc458036a570077a 100644 (file)
@@ -582,30 +582,9 @@ fn validate_operand(&self, operand: &Operand<'tcx>) -> Result<(), Unpromotable>
             Operand::Copy(place) |
             Operand::Move(place) => self.validate_place(place.as_ref()),
 
-            Operand::Constant(constant) => {
-                if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val {
-                    if self.tcx.trait_of_item(def_id).is_some() {
-                        // Don't peek inside trait associated constants.
-                        // (see below what we do for other consts, for now)
-                    } else {
-                        // HACK(eddyb) ensure that errors propagate correctly.
-                        // FIXME(eddyb) remove this once the old promotion logic
-                        // is gone - we can always promote constants even if they
-                        // fail to pass const-checking, as compilation would've
-                        // errored independently and promotion can't change that.
-                        let bits = self.tcx.at(constant.span).mir_const_qualif(def_id);
-                        if bits == super::qualify_consts::QUALIF_ERROR_BIT {
-                            self.tcx.sess.delay_span_bug(
-                                constant.span,
-                                "promote_consts: MIR had errors",
-                            );
-                            return Err(Unpromotable);
-                        }
-                    }
-                }
-
-                Ok(())
-            }
+            // The qualifs for a constant (e.g. `HasMutInterior`) are checked in
+            // `validate_rvalue` upon access.
+            Operand::Constant(_) => Ok(()),
         }
     }
 
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
deleted file mode 100644 (file)
index 964efde..0000000
+++ /dev/null
@@ -1,1479 +0,0 @@
-//! A pass that qualifies constness of temporaries in constants,
-//! static initializers and functions and also drives promotion.
-//!
-//! The Qualif flags below can be used to also provide better
-//! diagnostics as to why a constant rvalue wasn't promoted.
-
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
-use rustc_target::spec::abi::Abi;
-use rustc::hir;
-use rustc::hir::def_id::DefId;
-use rustc::traits::{self, TraitEngine};
-use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
-use rustc::ty::cast::CastTy;
-use rustc::ty::query::Providers;
-use rustc::mir::*;
-use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
-use rustc::middle::lang_items;
-use rustc::session::config::nightly_options;
-use syntax::feature_gate::{emit_feature_err, GateIssue};
-use syntax::symbol::sym;
-use syntax_pos::{Span, DUMMY_SP};
-
-use std::borrow::Cow;
-use std::cell::Cell;
-use std::fmt;
-use std::ops::{Deref, Index, IndexMut};
-use std::usize;
-
-use rustc::hir::HirId;
-use crate::transform::{MirPass, MirSource};
-use crate::transform::check_consts::ops::{self, NonConstOp};
-
-use rustc_error_codes::*;
-
-/// What kind of item we are in.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum Mode {
-    /// A `static` item.
-    Static,
-    /// A `static mut` item.
-    StaticMut,
-    /// A `const fn` item.
-    ConstFn,
-    /// A `const` item or an anonymous constant (e.g. in array lengths).
-    Const,
-    /// Other type of `fn`.
-    NonConstFn,
-}
-
-impl Mode {
-    /// Determine whether we have to do full const-checking because syntactically, we
-    /// are required to be "const".
-    #[inline]
-    fn requires_const_checking(self) -> bool {
-        self != Mode::NonConstFn
-    }
-}
-
-impl fmt::Display for Mode {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            Mode::Const => write!(f, "constant"),
-            Mode::Static | Mode::StaticMut => write!(f, "static"),
-            Mode::ConstFn => write!(f, "constant function"),
-            Mode::NonConstFn => write!(f, "function")
-        }
-    }
-}
-
-const QUALIF_COUNT: usize = 2;
-
-// FIXME(eddyb) once we can use const generics, replace this array with
-// something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
-#[derive(Copy, Clone, Default)]
-struct PerQualif<T>([T; QUALIF_COUNT]);
-
-impl<T: Clone> PerQualif<T> {
-    fn new(x: T) -> Self {
-        PerQualif([x.clone(), x])
-    }
-}
-
-impl<T> PerQualif<T> {
-    fn as_mut(&mut self) -> PerQualif<&mut T> {
-        let [x0, x1] = &mut self.0;
-        PerQualif([x0, x1])
-    }
-
-    fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
-        let [x0, x1] = self.0;
-        let [y0, y1] = other.0;
-        PerQualif([(x0, y0), (x1, y1)])
-    }
-}
-
-impl PerQualif<bool> {
-    fn encode_to_bits(self) -> u8 {
-        self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
-            bits | ((qualif as u8) << i)
-        })
-    }
-
-    fn decode_from_bits(bits: u8) -> Self {
-        let mut qualifs = Self::default();
-        for (i, qualif) in qualifs.0.iter_mut().enumerate() {
-            *qualif = (bits & (1 << i)) != 0;
-        }
-        qualifs
-    }
-}
-
-impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
-    type Output = T;
-
-    fn index(&self, _: Q) -> &T {
-        &self.0[Q::IDX]
-    }
-}
-
-impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
-    fn index_mut(&mut self, _: Q) -> &mut T {
-        &mut self.0[Q::IDX]
-    }
-}
-
-struct ConstCx<'a, 'tcx> {
-    tcx: TyCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    mode: Mode,
-    body: &'a Body<'tcx>,
-
-    per_local: PerQualif<BitSet<Local>>,
-}
-
-impl<'a, 'tcx> ConstCx<'a, 'tcx> {
-    fn is_const_panic_fn(&self, def_id: DefId) -> bool {
-        Some(def_id) == self.tcx.lang_items().panic_fn() ||
-        Some(def_id) == self.tcx.lang_items().begin_panic_fn()
-    }
-}
-
-#[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>],
-        return_ty: Ty<'tcx>,
-    },
-}
-
-/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
-/// code for promotion or prevent it from evaluating at compile time. So `return true` means
-/// "I found something bad, no reason to go on searching". `false` is only returned if we
-/// definitely cannot find anything bad anywhere.
-///
-/// The default implementations proceed structurally.
-trait Qualif {
-    const IDX: usize;
-
-    /// Return the qualification that is (conservatively) correct for any value
-    /// of the type, or `None` if the qualification is not value/type-based.
-    fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
-        None
-    }
-
-    /// Return a mask for the qualification, given a type. This is `false` iff
-    /// no value of that type can have the qualification.
-    fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
-        Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
-    }
-
-    fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
-        cx.per_local.0[Self::IDX].contains(local)
-    }
-
-    fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
-        // FIXME(eddyb) should we do anything here for value properties?
-        false
-    }
-
-    fn in_projection_structurally(
-        cx: &ConstCx<'_, 'tcx>,
-        place: PlaceRef<'_, 'tcx>,
-    ) -> bool {
-        if let [proj_base @ .., elem] = place.projection {
-            let base_qualif = Self::in_place(cx, PlaceRef {
-                base: place.base,
-                projection: proj_base,
-            });
-            let qualif = base_qualif && Self::mask_for_ty(
-                cx,
-                Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
-                    .projection_ty(cx.tcx, elem)
-                    .ty,
-            );
-            match elem {
-                ProjectionElem::Deref |
-                ProjectionElem::Subslice { .. } |
-                ProjectionElem::Field(..) |
-                ProjectionElem::ConstantIndex { .. } |
-                ProjectionElem::Downcast(..) => qualif,
-
-                // FIXME(eddyb) shouldn't this be masked *after* including the
-                // index local? Then again, it's `usize` which is neither
-                // `HasMutInterior` nor `NeedsDrop`.
-                ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local),
-            }
-        } else {
-            bug!("This should be called if projection is not empty");
-        }
-    }
-
-    fn in_projection(
-        cx: &ConstCx<'_, 'tcx>,
-        place: PlaceRef<'_, 'tcx>,
-    ) -> bool {
-        Self::in_projection_structurally(cx, place)
-    }
-
-    fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
-        match place {
-            PlaceRef {
-                base: PlaceBase::Local(local),
-                projection: [],
-            } => Self::in_local(cx, *local),
-            PlaceRef {
-                base: PlaceBase::Static(box Static {
-                    kind: StaticKind::Promoted(..),
-                    ..
-                }),
-                projection: [],
-            } => bug!("qualifying already promoted MIR"),
-            PlaceRef {
-                base: PlaceBase::Static(static_),
-                projection: [],
-            } => {
-                Self::in_static(cx, static_)
-            },
-            PlaceRef {
-                base: _,
-                projection: [.., _],
-            } => Self::in_projection(cx, place),
-        }
-    }
-
-    fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
-        match *operand {
-            Operand::Copy(ref place) |
-            Operand::Move(ref place) => Self::in_place(cx, place.as_ref()),
-
-            Operand::Constant(ref constant) => {
-                if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val {
-                    // Don't peek inside trait associated constants.
-                    if cx.tcx.trait_of_item(def_id).is_some() {
-                        Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
-                    } else {
-                        let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
-
-                        let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
-
-                        // Just in case the type is more specific than
-                        // the definition, e.g., impl associated const
-                        // with type parameters, take it into account.
-                        qualif && Self::mask_for_ty(cx, constant.literal.ty)
-                    }
-                } else {
-                    false
-                }
-            }
-        }
-    }
-
-    fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
-        match *rvalue {
-            Rvalue::NullaryOp(..) => false,
-
-            Rvalue::Discriminant(ref place) |
-            Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()),
-
-            Rvalue::Use(ref operand) |
-            Rvalue::Repeat(ref operand, _) |
-            Rvalue::UnaryOp(_, ref operand) |
-            Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
-
-            Rvalue::BinaryOp(_, ref lhs, ref rhs) |
-            Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
-                Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
-            }
-
-            Rvalue::Ref(_, _, ref place) => {
-                // Special-case reborrows to be more like a copy of the reference.
-                if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
-                    if ProjectionElem::Deref == elem {
-                        let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
-                        if let ty::Ref(..) = base_ty.kind {
-                            return Self::in_place(cx, PlaceRef {
-                                base: &place.base,
-                                projection: proj_base,
-                            });
-                        }
-                    }
-                }
-
-                Self::in_place(cx, place.as_ref())
-            }
-
-            Rvalue::Aggregate(_, ref operands) => {
-                operands.iter().any(|o| Self::in_operand(cx, o))
-            }
-        }
-    }
-
-    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
-        Self::in_rvalue_structurally(cx, rvalue)
-    }
-
-    fn in_call(
-        cx: &ConstCx<'_, 'tcx>,
-        _callee: &Operand<'tcx>,
-        _args: &[Operand<'tcx>],
-        return_ty: Ty<'tcx>,
-    ) -> bool {
-        // Be conservative about the returned value of a const fn.
-        Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
-    }
-
-    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)
-            }
-        }
-    }
-}
-
-/// Constant containing interior mutability (`UnsafeCell<T>`).
-/// This must be ruled out to make sure that evaluating the constant at compile-time
-/// and at *any point* during the run-time would produce the same result. In particular,
-/// promotion of temporaries must not change program behavior; if the promoted could be
-/// written to, that would be a problem.
-struct HasMutInterior;
-
-impl Qualif for HasMutInterior {
-    const IDX: usize = 0;
-
-    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
-        Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
-    }
-
-    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
-        match *rvalue {
-            // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
-            // allowed in constants (and the `Checker` will error), and/or it
-            // won't be promoted, due to `&mut ...` or interior mutability.
-            Rvalue::Ref(_, kind, ref place) => {
-                let ty = place.ty(cx.body, cx.tcx).ty;
-
-                if let BorrowKind::Mut { .. } = kind {
-                    // In theory, any zero-sized value could be borrowed
-                    // mutably without consequences. However, only &mut []
-                    // is allowed right now, and only in functions.
-                    if cx.mode == Mode::StaticMut {
-                        // Inside a `static mut`, &mut [...] is also allowed.
-                        match ty.kind {
-                            ty::Array(..) | ty::Slice(_) => {}
-                            _ => return true,
-                        }
-                    } else if let ty::Array(_, len) = ty.kind {
-                        // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
-                        // seems unnecessary, given that this is merely a ZST.
-                        match len.try_eval_usize(cx.tcx, cx.param_env) {
-                            Some(0) if cx.mode == Mode::NonConstFn => {},
-                            _ => return true,
-                        }
-                    } else {
-                        return true;
-                    }
-                }
-            }
-
-            Rvalue::Aggregate(ref kind, _) => {
-                if let AggregateKind::Adt(def, ..) = **kind {
-                    if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
-                        let ty = rvalue.ty(cx.body, cx.tcx);
-                        assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
-                        return true;
-                    }
-                }
-            }
-
-            _ => {}
-        }
-
-        Self::in_rvalue_structurally(cx, rvalue)
-    }
-}
-
-/// Constant containing an ADT that implements `Drop`.
-/// This must be ruled out (a) because we cannot run `Drop` during compile-time
-/// as that might not be a `const fn`, and (b) because implicit promotion would
-/// remove side-effects that occur as part of dropping that value.
-struct NeedsDrop;
-
-impl Qualif for NeedsDrop {
-    const IDX: usize = 1;
-
-    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
-        Some(ty.needs_drop(cx.tcx, cx.param_env))
-    }
-
-    fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
-        if let Rvalue::Aggregate(ref kind, _) = *rvalue {
-            if let AggregateKind::Adt(def, ..) = **kind {
-                if def.has_dtor(cx.tcx) {
-                    return true;
-                }
-            }
-        }
-
-        Self::in_rvalue_structurally(cx, rvalue)
-    }
-}
-
-// Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
-macro_rules! static_assert_seq_qualifs {
-    ($i:expr => $first:ident $(, $rest:ident)*) => {
-        static_assert!({
-            static_assert_seq_qualifs!($i + 1 => $($rest),*);
-
-            $first::IDX == $i
-        });
-    };
-    ($i:expr =>) => {
-        static_assert!(QUALIF_COUNT == $i);
-    };
-}
-static_assert_seq_qualifs!(
-    0 => HasMutInterior, NeedsDrop
-);
-
-impl ConstCx<'_, 'tcx> {
-    fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
-        let mut qualifs = PerQualif::default();
-        qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
-        qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
-        qualifs
-    }
-
-    fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
-        let mut qualifs = PerQualif::default();
-        qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
-        qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
-        qualifs
-    }
-
-    fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
-        let mut qualifs = PerQualif::default();
-        qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
-        qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
-        qualifs
-    }
-}
-
-/// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
-/// for value qualifications, and accumulates writes of
-/// rvalue/call results to locals, in `local_qualif`.
-/// It also records candidates for promotion in `promotion_candidates`,
-/// both in functions and const/static items.
-struct Checker<'a, 'tcx> {
-    cx: ConstCx<'a, 'tcx>,
-
-    span: Span,
-    def_id: DefId,
-
-    /// If `true`, do not emit errors to the user, merely collect them in `errors`.
-    suppress_errors: bool,
-    errors: Vec<(Span, String)>,
-}
-
-macro_rules! unleash_miri {
-    ($this:expr) => {{
-        if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
-            if $this.mode.requires_const_checking() && !$this.suppress_errors {
-                $this.tcx.sess.span_warn($this.span, "skipping const checks");
-            }
-            return;
-        }
-    }}
-}
-
-impl Deref for Checker<'a, 'tcx> {
-    type Target = ConstCx<'a, 'tcx>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.cx
-    }
-}
-
-impl<'a, 'tcx> Checker<'a, 'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
-        assert!(def_id.is_local());
-
-        let param_env = tcx.param_env(def_id);
-
-        let mut cx = ConstCx {
-            tcx,
-            param_env,
-            mode,
-            body,
-            per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
-        };
-
-        for (local, decl) in body.local_decls.iter_enumerated() {
-            if let LocalKind::Arg = body.local_kind(local) {
-                let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
-                for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
-                    if *qualif {
-                        per_local.insert(local);
-                    }
-                }
-            }
-        }
-
-        Checker {
-            cx,
-            span: body.span,
-            def_id,
-            errors: vec![],
-            suppress_errors: false,
-        }
-    }
-
-    // FIXME(eddyb) we could split the errors into meaningful
-    // categories, but enabling full miri would make that
-    // slightly pointless (even with feature-gating).
-    fn not_const(&mut self, op: impl NonConstOp) {
-        unleash_miri!(self);
-        if self.mode.requires_const_checking() && !self.suppress_errors {
-            self.record_error(op);
-            let mut err = struct_span_err!(
-                self.tcx.sess,
-                self.span,
-                E0019,
-                "{} contains unimplemented expression type",
-                self.mode
-            );
-            if self.tcx.sess.teach(&err.get_code().unwrap()) {
-                err.note("A function call isn't allowed in the const's initialization expression \
-                          because the expression's value must be known at compile-time.");
-                err.note("Remember: you can't use a function call inside a const's initialization \
-                          expression! However, you can use it anywhere else.");
-            }
-            err.emit();
-        }
-    }
-
-    fn record_error(&mut self, op: impl NonConstOp) {
-        self.record_error_spanned(op, self.span);
-    }
-
-    fn record_error_spanned(&mut self, op: impl NonConstOp, span: Span) {
-        self.errors.push((span, format!("{:?}", op)));
-    }
-
-    /// Assigns an rvalue/call qualification to the given destination.
-    fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
-        trace!("assign: {:?} <- {:?}", dest, source);
-
-        let mut qualifs = self.qualifs_in_value(source);
-
-        match source {
-            ValueSource::Rvalue(&Rvalue::Ref(_, kind, _)) => {
-                // Getting `true` from `HasMutInterior::in_rvalue` means
-                // the borrowed place is disallowed from being borrowed,
-                // due to either a mutable borrow (with some exceptions),
-                // or an shared borrow of a value with interior mutability.
-                // Then `HasMutInterior` is cleared
-                // to avoid duplicate errors (e.g. from reborrowing).
-                if qualifs[HasMutInterior] {
-                    qualifs[HasMutInterior] = false;
-
-                    debug!("suppress_errors: {}", self.suppress_errors);
-                    if self.mode.requires_const_checking() && !self.suppress_errors {
-                        if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
-                            self.record_error(ops::MutBorrow(kind));
-                            if let BorrowKind::Mut { .. } = kind {
-                                let mut err = struct_span_err!(self.tcx.sess,  self.span, E0017,
-                                                               "references in {}s may only refer \
-                                                                to immutable values", self.mode);
-                                err.span_label(self.span, format!("{}s require immutable values",
-                                                                    self.mode));
-                                if self.tcx.sess.teach(&err.get_code().unwrap()) {
-                                    err.note("References in statics and constants may only refer \
-                                              to immutable values.\n\n\
-                                              Statics are shared everywhere, and if they refer to \
-                                              mutable data one might violate memory safety since \
-                                              holding multiple mutable references to shared data \
-                                              is not allowed.\n\n\
-                                              If you really want global mutable state, try using \
-                                              static mut or a global UnsafeCell.");
-                                }
-                                err.emit();
-                            } else {
-                                span_err!(self.tcx.sess, self.span, E0492,
-                                          "cannot borrow a constant which may contain \
-                                           interior mutability, create a static instead");
-                            }
-                        }
-                    }
-                }
-            },
-            _ => {},
-        }
-
-        let mut dest_projection = &dest.projection[..];
-        let index = loop {
-            match (&dest.base, dest_projection) {
-                // We treat all locals equal in constants
-                (&PlaceBase::Local(index), []) => break index,
-                // projections are transparent for assignments
-                // we qualify the entire destination at once, even if just a field would have
-                // stricter qualification
-                (base, [proj_base @ .., _]) => {
-                    // Catch more errors in the destination. `visit_place` also checks various
-                    // projection rules like union field access and raw pointer deref
-                    let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
-                    self.visit_place_base(base, context, location);
-                    self.visit_projection(base, dest_projection, context, location);
-                    dest_projection = proj_base;
-                },
-                (&PlaceBase::Static(box Static {
-                    kind: StaticKind::Promoted(..),
-                    ..
-                }), []) => bug!("promoteds don't exist yet during promotion"),
-                (&PlaceBase::Static(box Static{ kind: _, .. }), []) => {
-                    // Catch more errors in the destination. `visit_place` also checks that we
-                    // do not try to access statics from constants or try to mutate statics
-                    let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
-                    self.visit_place_base(&dest.base, context, location);
-                    return;
-                }
-            }
-        };
-
-        let kind = self.body.local_kind(index);
-        debug!("store to {:?} {:?}", kind, index);
-
-        // this is overly restrictive, because even full assignments do not clear the qualif
-        // While we could special case full assignments, this would be inconsistent with
-        // aggregates where we overwrite all fields via assignments, which would not get
-        // that feature.
-        for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
-            if *qualif {
-                per_local.insert(index);
-            }
-        }
-    }
-
-    /// Check a whole const, static initializer or const fn.
-    fn check_const(&mut self) -> u8 {
-        use crate::transform::check_consts as new_checker;
-
-        debug!("const-checking {} {:?}", self.mode, self.def_id);
-
-        // FIXME: Also use the new validator when features that require it (e.g. `const_if`) are
-        // enabled.
-        let use_new_validator = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
-        if use_new_validator {
-            debug!("Using dataflow-based const validator");
-        }
-
-        let item = new_checker::Item::new(self.tcx, self.def_id, self.body);
-        let mut validator = new_checker::validation::Validator::new(&item);
-
-        validator.suppress_errors = !use_new_validator;
-        self.suppress_errors = use_new_validator;
-
-        let body = self.body;
-
-        let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
-        let mut bb = START_BLOCK;
-        loop {
-            seen_blocks.insert(bb.index());
-
-            self.visit_basic_block_data(bb, &body[bb]);
-            validator.visit_basic_block_data(bb, &body[bb]);
-
-            let target = match body[bb].terminator().kind {
-                TerminatorKind::Goto { target } |
-                TerminatorKind::FalseUnwind { real_target: target, .. } |
-                TerminatorKind::Drop { target, .. } |
-                TerminatorKind::DropAndReplace { target, .. } |
-                TerminatorKind::Assert { target, .. } |
-                TerminatorKind::Call { destination: Some((_, target)), .. } => {
-                    Some(target)
-                }
-
-                // Non-terminating calls cannot produce any value.
-                TerminatorKind::Call { destination: None, .. } => {
-                    break;
-                }
-
-                TerminatorKind::SwitchInt {..} |
-                TerminatorKind::Resume |
-                TerminatorKind::Abort |
-                TerminatorKind::GeneratorDrop |
-                TerminatorKind::Yield { .. } |
-                TerminatorKind::Unreachable |
-                TerminatorKind::FalseEdges { .. } => None,
-
-                TerminatorKind::Return => {
-                    break;
-                }
-            };
-
-            match target {
-                // No loops allowed.
-                Some(target) if !seen_blocks.contains(target.index()) => {
-                    bb = target;
-                }
-                _ => {
-                    if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
-                        self.tcx.sess.delay_span_bug(
-                            self.span,
-                            "complex control flow is forbidden in a const context",
-                        );
-                    }
-                    break;
-                }
-            }
-        }
-
-        // The new validation pass should agree with the old when running on simple const bodies
-        // (e.g. no `if` or `loop`).
-        if !use_new_validator {
-            let mut new_errors = validator.take_errors();
-
-            // FIXME: each checker sometimes emits the same error with the same span twice in a row.
-            self.errors.dedup();
-            new_errors.dedup();
-
-            if self.errors != new_errors {
-                validator_mismatch(
-                    self.tcx,
-                    body,
-                    std::mem::replace(&mut self.errors, vec![]),
-                    new_errors,
-                );
-            }
-        }
-
-        self.qualifs_in_local(RETURN_PLACE).encode_to_bits()
-    }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
-    fn visit_place_base(
-        &mut self,
-        place_base: &PlaceBase<'tcx>,
-        context: PlaceContext,
-        location: Location,
-    ) {
-        self.super_place_base(place_base, context, location);
-        match place_base {
-            PlaceBase::Local(_) => {}
-            PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
-                unreachable!()
-            }
-            PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
-                if self.tcx
-                        .get_attrs(*def_id)
-                        .iter()
-                        .any(|attr| attr.check_name(sym::thread_local)) {
-                    if self.mode.requires_const_checking() && !self.suppress_errors {
-                        self.record_error(ops::ThreadLocalAccess);
-                        span_err!(self.tcx.sess, self.span, E0625,
-                                    "thread-local statics cannot be \
-                                    accessed at compile-time");
-                    }
-                    return;
-                }
-
-                // Only allow statics (not consts) to refer to other statics.
-                if self.mode == Mode::Static || self.mode == Mode::StaticMut {
-                    return;
-                }
-                unleash_miri!(self);
-
-                if self.mode.requires_const_checking() && !self.suppress_errors {
-                    self.record_error(ops::StaticAccess);
-                    let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
-                                                    "{}s cannot refer to statics, use \
-                                                    a constant instead", self.mode);
-                    if self.tcx.sess.teach(&err.get_code().unwrap()) {
-                        err.note(
-                            "Static and const variables can refer to other const variables. \
-                                But a const variable cannot refer to a static variable."
-                        );
-                        err.help(
-                            "To fix this, the value can be extracted as a const and then used."
-                        );
-                    }
-                    err.emit()
-                }
-            }
-        }
-    }
-
-    fn visit_projection_elem(
-        &mut self,
-        place_base: &PlaceBase<'tcx>,
-        proj_base: &[PlaceElem<'tcx>],
-        elem: &PlaceElem<'tcx>,
-        context: PlaceContext,
-        location: Location,
-    ) {
-        debug!(
-            "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \
-            context={:?} location={:?}",
-            place_base,
-            proj_base,
-            elem,
-            context,
-            location,
-        );
-
-        self.super_projection_elem(place_base, proj_base, elem, context, location);
-
-        match elem {
-            ProjectionElem::Deref => {
-                if context.is_mutating_use() {
-                    // `not_const` errors out in const contexts
-                    self.not_const(ops::MutDeref)
-                }
-                let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
-                match self.mode {
-                    Mode::NonConstFn => {}
-                    _ if self.suppress_errors => {}
-                    _ => {
-                        if let ty::RawPtr(_) = base_ty.kind {
-                            if !self.tcx.features().const_raw_ptr_deref {
-                                self.record_error(ops::RawPtrDeref);
-                                emit_feature_err(
-                                    &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
-                                    self.span, GateIssue::Language,
-                                    &format!(
-                                        "dereferencing raw pointers in {}s is unstable",
-                                        self.mode,
-                                    ),
-                                );
-                            }
-                        }
-                    }
-                }
-            }
-
-            ProjectionElem::ConstantIndex {..} |
-            ProjectionElem::Subslice {..} |
-            ProjectionElem::Field(..) |
-            ProjectionElem::Index(_) => {
-                let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
-                if let Some(def) = base_ty.ty_adt_def() {
-                    if def.is_union() {
-                        match self.mode {
-                            Mode::ConstFn => {
-                                if !self.tcx.features().const_fn_union
-                                    && !self.suppress_errors
-                                {
-                                    self.record_error(ops::UnionAccess);
-                                    emit_feature_err(
-                                        &self.tcx.sess.parse_sess, sym::const_fn_union,
-                                        self.span, GateIssue::Language,
-                                        "unions in const fn are unstable",
-                                    );
-                                }
-                            },
-
-                            | Mode::NonConstFn
-                            | Mode::Static
-                            | Mode::StaticMut
-                            | Mode::Const
-                            => {},
-                        }
-                    }
-                }
-            }
-
-            ProjectionElem::Downcast(..) => {
-                self.not_const(ops::Downcast)
-            }
-        }
-    }
-
-    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
-        debug!("visit_operand: operand={:?} location={:?}", operand, location);
-        self.super_operand(operand, location);
-
-        match *operand {
-            Operand::Move(ref place) => {
-                // Mark the consumed locals to indicate later drops are noops.
-                if let Some(local) = place.as_local() {
-                    self.cx.per_local[NeedsDrop].remove(local);
-                }
-            }
-            Operand::Copy(_) |
-            Operand::Constant(_) => {}
-        }
-    }
-
-    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
-        debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
-
-        // Check nested operands and places.
-        if let Rvalue::Ref(_, kind, ref place) = *rvalue {
-            // Special-case reborrows.
-            let mut reborrow_place = None;
-            if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
-                if elem == ProjectionElem::Deref {
-                    let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
-                    if let ty::Ref(..) = base_ty.kind {
-                        reborrow_place = Some(proj_base);
-                    }
-                }
-            }
-
-            if let Some(proj) = reborrow_place {
-                let ctx = match kind {
-                    BorrowKind::Shared => PlaceContext::NonMutatingUse(
-                        NonMutatingUseContext::SharedBorrow,
-                    ),
-                    BorrowKind::Shallow => PlaceContext::NonMutatingUse(
-                        NonMutatingUseContext::ShallowBorrow,
-                    ),
-                    BorrowKind::Unique => PlaceContext::NonMutatingUse(
-                        NonMutatingUseContext::UniqueBorrow,
-                    ),
-                    BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
-                        MutatingUseContext::Borrow,
-                    ),
-                };
-                self.visit_place_base(&place.base, ctx, location);
-                self.visit_projection(&place.base, proj, ctx, location);
-            } else {
-                self.super_rvalue(rvalue, location);
-            }
-        } else {
-            self.super_rvalue(rvalue, location);
-        }
-
-        match *rvalue {
-            Rvalue::Use(_) |
-            Rvalue::Repeat(..) |
-            Rvalue::UnaryOp(UnOp::Neg, _) |
-            Rvalue::UnaryOp(UnOp::Not, _) |
-            Rvalue::NullaryOp(NullOp::SizeOf, _) |
-            Rvalue::CheckedBinaryOp(..) |
-            Rvalue::Cast(CastKind::Pointer(_), ..) |
-            Rvalue::Discriminant(..) |
-            Rvalue::Len(_) |
-            Rvalue::Ref(..) |
-            Rvalue::Aggregate(..) => {}
-
-            Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
-                let operand_ty = operand.ty(self.body, self.tcx);
-                let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
-                let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
-                match (cast_in, cast_out) {
-                    (CastTy::Ptr(_), CastTy::Int(_)) |
-                    (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
-                        unleash_miri!(self);
-                        if !self.tcx.features().const_raw_ptr_to_usize_cast
-                            && !self.suppress_errors
-                        {
-                            // in const fn and constants require the feature gate
-                            // FIXME: make it unsafe inside const fn and constants
-                            self.record_error(ops::RawPtrToIntCast);
-                            emit_feature_err(
-                                &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
-                                self.span, GateIssue::Language,
-                                &format!(
-                                    "casting pointers to integers in {}s is unstable",
-                                    self.mode,
-                                ),
-                            );
-                        }
-                    }
-                    _ => {}
-                }
-            }
-
-            Rvalue::BinaryOp(op, ref lhs, _) => {
-                if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
-                    assert!(op == BinOp::Eq || op == BinOp::Ne ||
-                            op == BinOp::Le || op == BinOp::Lt ||
-                            op == BinOp::Ge || op == BinOp::Gt ||
-                            op == BinOp::Offset);
-
-                    unleash_miri!(self);
-                    if self.mode.requires_const_checking() &&
-                        !self.tcx.features().const_compare_raw_pointers &&
-                        !self.suppress_errors
-                    {
-                        self.record_error(ops::RawPtrComparison);
-                        // require the feature gate inside constants and const fn
-                        // FIXME: make it unsafe to use these operations
-                        emit_feature_err(
-                            &self.tcx.sess.parse_sess,
-                            sym::const_compare_raw_pointers,
-                            self.span,
-                            GateIssue::Language,
-                            &format!("comparing raw pointers inside {}", self.mode),
-                        );
-                    }
-                }
-            }
-
-            Rvalue::NullaryOp(NullOp::Box, _) => {
-                unleash_miri!(self);
-                if self.mode.requires_const_checking() && !self.suppress_errors {
-                    self.record_error(ops::HeapAllocation);
-                    let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
-                                                   "allocations are not allowed in {}s", self.mode);
-                    err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
-                    if self.tcx.sess.teach(&err.get_code().unwrap()) {
-                        err.note(
-                            "The value of statics and constants must be known at compile time, \
-                             and they live for the entire lifetime of a program. Creating a boxed \
-                             value allocates memory on the heap at runtime, and therefore cannot \
-                             be done at compile time."
-                        );
-                    }
-                    err.emit();
-                }
-            }
-        }
-    }
-
-    fn visit_terminator_kind(&mut self,
-                             kind: &TerminatorKind<'tcx>,
-                             location: Location) {
-        debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
-        if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
-            if let Some((ref dest, _)) = *destination {
-                self.assign(dest, ValueSource::Call {
-                    callee: func,
-                    args,
-                    return_ty: dest.ty(self.body, self.tcx).ty,
-                }, location);
-            }
-
-            let fn_ty = func.ty(self.body, self.tcx);
-            match fn_ty.kind {
-                ty::FnDef(def_id, _) => {
-                    match self.tcx.fn_sig(def_id).abi() {
-                        Abi::RustIntrinsic |
-                        Abi::PlatformIntrinsic => {
-                            assert!(!self.tcx.is_const_fn(def_id));
-                            match &*self.tcx.item_name(def_id).as_str() {
-                                // special intrinsic that can be called diretly without an intrinsic
-                                // feature gate needs a language feature gate
-                                "transmute" => {
-                                    if self.mode.requires_const_checking()
-                                        && !self.suppress_errors
-                                    {
-                                        // const eval transmute calls only with the feature gate
-                                        if !self.tcx.features().const_transmute {
-                                            self.record_error(ops::Transmute);
-                                            emit_feature_err(
-                                                &self.tcx.sess.parse_sess, sym::const_transmute,
-                                                self.span, GateIssue::Language,
-                                                &format!("The use of std::mem::transmute() \
-                                                is gated in {}s", self.mode));
-                                        }
-                                    }
-                                }
-
-                                // no need to check feature gates, intrinsics are only callable
-                                // from the libstd or with forever unstable feature gates
-                                _ => {}
-                            }
-                        }
-                        _ => {
-                            // In normal functions no calls are feature-gated.
-                            if self.mode.requires_const_checking() {
-                                let unleash_miri = self
-                                    .tcx
-                                    .sess
-                                    .opts
-                                    .debugging_opts
-                                    .unleash_the_miri_inside_of_you;
-                                if self.tcx.is_const_fn(def_id)
-                                    || unleash_miri
-                                    || self.suppress_errors
-                                {
-                                    // stable const fns or unstable const fns
-                                    // with their feature gate active
-                                    // FIXME(eddyb) move stability checks from `is_const_fn` here.
-                                } else if self.is_const_panic_fn(def_id) {
-                                    // Check the const_panic feature gate.
-                                    // FIXME: cannot allow this inside `allow_internal_unstable`
-                                    // because that would make `panic!` insta stable in constants,
-                                    // since the macro is marked with the attribute.
-                                    if !self.tcx.features().const_panic {
-                                        // Don't allow panics in constants without the feature gate.
-                                        self.record_error(ops::Panic);
-                                        emit_feature_err(
-                                            &self.tcx.sess.parse_sess,
-                                            sym::const_panic,
-                                            self.span,
-                                            GateIssue::Language,
-                                            &format!("panicking in {}s is unstable", self.mode),
-                                        );
-                                    }
-                                } else if let Some(feature)
-                                              = self.tcx.is_unstable_const_fn(def_id) {
-                                    // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
-                                    // functions without the feature gate active in this crate in
-                                    // order to report a better error message than the one below.
-                                    if !self.span.allows_unstable(feature) {
-                                        self.record_error(ops::FnCallUnstable(def_id, feature));
-                                        let mut err = self.tcx.sess.struct_span_err(self.span,
-                                            &format!("`{}` is not yet stable as a const fn",
-                                                    self.tcx.def_path_str(def_id)));
-                                        if nightly_options::is_nightly_build() {
-                                            help!(&mut err,
-                                                  "add `#![feature({})]` to the \
-                                                   crate attributes to enable",
-                                                  feature);
-                                        }
-                                        err.emit();
-                                    }
-                                } else {
-                                    self.record_error(ops::FnCallNonConst(def_id));
-                                    let mut err = struct_span_err!(
-                                        self.tcx.sess,
-                                        self.span,
-                                        E0015,
-                                        "calls in {}s are limited to constant functions, \
-                                         tuple structs and tuple variants",
-                                        self.mode,
-                                    );
-                                    err.emit();
-                                }
-                            }
-                        }
-                    }
-                }
-                ty::FnPtr(_) => {
-                    unleash_miri!(self);
-                    if self.mode.requires_const_checking() && !self.suppress_errors {
-                        self.record_error(ops::FnCallIndirect);
-                        let mut err = self.tcx.sess.struct_span_err(
-                            self.span,
-                            "function pointers are not allowed in const fn"
-                        );
-                        err.emit();
-                    }
-                }
-                _ => {
-                    self.not_const(ops::FnCallOther);
-                }
-            }
-
-            // Check callee and argument operands.
-            self.visit_operand(func, location);
-            for arg in args {
-                self.visit_operand(arg, 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() && !self.suppress_errors {
-                unleash_miri!(self);
-                // HACK(eddyb): emulate a bit of dataflow analysis,
-                // conservatively, that drop elaboration will do.
-                let needs_drop = if let Some(local) = place.as_local() {
-                    if NeedsDrop::in_local(self, local) {
-                        Some(self.body.local_decls[local].source_info.span)
-                    } else {
-                        None
-                    }
-                } else {
-                    Some(self.span)
-                };
-
-                if let Some(span) = needs_drop {
-                    // Double-check the type being dropped, to minimize false positives.
-                    let ty = place.ty(self.body, self.tcx).ty;
-                    if ty.needs_drop(self.tcx, self.param_env) {
-                        self.record_error_spanned(ops::LiveDrop, span);
-                        struct_span_err!(self.tcx.sess, span, E0493,
-                                         "destructors cannot be evaluated at compile-time")
-                            .span_label(span, format!("{}s cannot evaluate destructors",
-                                                      self.mode))
-                            .emit();
-                    }
-                }
-            }
-
-            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);
-        }
-    }
-
-    fn visit_assign(&mut self,
-                    dest: &Place<'tcx>,
-                    rvalue: &Rvalue<'tcx>,
-                    location: Location) {
-        debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
-        self.assign(dest, ValueSource::Rvalue(rvalue), location);
-
-        self.visit_rvalue(rvalue, location);
-    }
-
-    fn visit_source_info(&mut self, source_info: &SourceInfo) {
-        debug!("visit_source_info: source_info={:?}", source_info);
-        self.span = source_info.span;
-    }
-
-    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
-        debug!("visit_statement: statement={:?} location={:?}", statement, location);
-        match statement.kind {
-            StatementKind::Assign(..) => {
-                self.super_statement(statement, location);
-            }
-            StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
-                if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
-                    self.tcx.sess.delay_span_bug(
-                        self.span,
-                        "complex control flow is forbidden in a const context",
-                    );
-                }
-            }
-            // FIXME(eddyb) should these really do nothing?
-            StatementKind::FakeRead(..) |
-            StatementKind::SetDiscriminant { .. } |
-            StatementKind::StorageLive(_) |
-            StatementKind::StorageDead(_) |
-            StatementKind::InlineAsm {..} |
-            StatementKind::Retag { .. } |
-            StatementKind::AscribeUserType(..) |
-            StatementKind::Nop => {}
-        }
-    }
-}
-
-pub fn provide(providers: &mut Providers<'_>) {
-    *providers = Providers {
-        mir_const_qualif,
-        ..*providers
-    };
-}
-
-// FIXME(eddyb) this is only left around for the validation logic
-// in `promote_consts`, see the comment in `validate_operand`.
-pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
-
-fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
-    // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
-    // cannot yet be stolen), because `mir_validated()`, which steals
-    // from `mir_const(), forces this query to execute before
-    // performing the steal.
-    let body = &tcx.mir_const(def_id).borrow();
-
-    if body.return_ty().references_error() {
-        tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
-        return QUALIF_ERROR_BIT;
-    }
-
-    Checker::new(tcx, def_id, body, Mode::Const).check_const()
-}
-
-pub struct QualifyAndPromoteConstants<'tcx> {
-    pub promoted: Cell<IndexVec<Promoted, Body<'tcx>>>,
-}
-
-impl<'tcx> Default for QualifyAndPromoteConstants<'tcx> {
-    fn default() -> Self {
-        QualifyAndPromoteConstants {
-            promoted: Cell::new(IndexVec::new()),
-        }
-    }
-}
-
-impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        // There's not really any point in promoting errorful MIR.
-        if body.return_ty().references_error() {
-            tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
-            return;
-        }
-
-        if src.promoted.is_some() {
-            return;
-        }
-
-        let def_id = src.def_id();
-        let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
-
-        let mode = determine_mode(tcx, hir_id, def_id);
-
-        debug!("run_pass: mode={:?}", mode);
-        if let Mode::NonConstFn = mode {
-            // No need to const-check a non-const `fn` now that we don't do promotion here.
-            return;
-        } else if let Mode::ConstFn = mode {
-            let mut checker = Checker::new(tcx, def_id, body, mode);
-            let use_min_const_fn_checks =
-                !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
-                tcx.is_min_const_fn(def_id);
-            if use_min_const_fn_checks {
-                // Enforce `min_const_fn` for stable `const fn`s.
-                use super::qualify_min_const_fn::is_min_const_fn;
-                if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
-                    error_min_const_fn_violation(tcx, span, err);
-                    return;
-                }
-            }
-
-            // `check_const` should not produce any errors, but better safe than sorry
-            // FIXME(#53819)
-            // Enforce a constant-like CFG for `const fn`.
-            checker.check_const();
-        } else {
-            check_short_circuiting_in_const_local(tcx, body, mode);
-
-            match mode {
-                Mode::Const => tcx.mir_const_qualif(def_id),
-                _ => Checker::new(tcx, def_id, body, mode).check_const(),
-            };
-        }
-
-        if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
-            // `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`.
-            check_static_is_sync(tcx, body, hir_id);
-        }
-    }
-}
-
-fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode {
-    match tcx.hir().body_owner_kind(hir_id) {
-        hir::BodyOwnerKind::Closure => Mode::NonConstFn,
-        hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
-        hir::BodyOwnerKind::Fn => Mode::NonConstFn,
-        hir::BodyOwnerKind::Const => Mode::Const,
-        hir::BodyOwnerKind::Static(hir::Mutability::Immutable) => Mode::Static,
-        hir::BodyOwnerKind::Static(hir::Mutability::Mutable) => Mode::StaticMut,
-    }
-}
-
-fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
-    struct_span_err!(tcx.sess, span, E0723, "{}", msg)
-        .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
-        .help("add `#![feature(const_fn)]` to the crate attributes to enable")
-        .emit();
-}
-
-fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) {
-    if body.control_flow_destroyed.is_empty() {
-        return;
-    }
-
-    let mut locals = body.vars_iter();
-    if let Some(local) = locals.next() {
-        let span = body.local_decls[local].source_info.span;
-        let mut error = tcx.sess.struct_span_err(
-            span,
-            &format!(
-                "new features like let bindings are not permitted in {}s \
-                which also use short circuiting operators",
-                mode,
-            ),
-        );
-        for (span, kind) in body.control_flow_destroyed.iter() {
-            error.span_note(
-                *span,
-                &format!("use of {} here does not actually short circuit due to \
-                the const evaluator presently not being able to do control flow. \
-                See https://github.com/rust-lang/rust/issues/49146 for more \
-                information.", kind),
-            );
-        }
-        for local in locals {
-            let span = body.local_decls[local].source_info.span;
-            error.span_note(span, "more locals defined here");
-        }
-        error.emit();
-    }
-}
-
-fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
-    let ty = body.return_ty();
-    tcx.infer_ctxt().enter(|infcx| {
-        let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
-        let mut fulfillment_cx = traits::FulfillmentContext::new();
-        let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
-        fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
-        if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
-            infcx.report_fulfillment_errors(&err, None, false);
-        }
-    });
-}
-
-fn validator_mismatch(
-    tcx: TyCtxt<'tcx>,
-    body: &Body<'tcx>,
-    mut old_errors: Vec<(Span, String)>,
-    mut new_errors: Vec<(Span, String)>,
-) {
-    error!("old validator: {:?}", old_errors);
-    error!("new validator: {:?}", new_errors);
-
-    // ICE on nightly if the validators do not emit exactly the same errors.
-    // Users can supress this panic with an unstable compiler flag (hopefully after
-    // filing an issue).
-    let opts = &tcx.sess.opts;
-    let strict_validation_enabled = opts.unstable_features.is_nightly_build()
-        && !opts.debugging_opts.suppress_const_validation_back_compat_ice;
-
-    if !strict_validation_enabled {
-        return;
-    }
-
-    // If this difference would cause a regression from the old to the new or vice versa, trigger
-    // the ICE.
-    if old_errors.is_empty() || new_errors.is_empty() {
-        span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
-    }
-
-    // HACK: Borrows that would allow mutation are forbidden in const contexts, but they cause the
-    // new validator to be more conservative about when a dropped local has been moved out of.
-    //
-    // Supress the mismatch ICE in cases where the validators disagree only on the number of
-    // `LiveDrop` errors and both observe the same sequence of `MutBorrow`s.
-
-    let is_live_drop = |(_, s): &mut (_, String)| s.starts_with("LiveDrop");
-    let is_mut_borrow = |(_, s): &&(_, String)| s.starts_with("MutBorrow");
-
-    let old_live_drops: Vec<_> = old_errors.drain_filter(is_live_drop).collect();
-    let new_live_drops: Vec<_> = new_errors.drain_filter(is_live_drop).collect();
-
-    let only_live_drops_differ = old_live_drops != new_live_drops && old_errors == new_errors;
-
-    let old_mut_borrows = old_errors.iter().filter(is_mut_borrow);
-    let new_mut_borrows = new_errors.iter().filter(is_mut_borrow);
-
-    let at_least_one_mut_borrow = old_mut_borrows.clone().next().is_some();
-
-    if only_live_drops_differ && at_least_one_mut_borrow && old_mut_borrows.eq(new_mut_borrows) {
-        return;
-    }
-
-    span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
-}
-
-const VALIDATOR_MISMATCH_ERR: &str =
-    r"Disagreement between legacy and dataflow-based const validators.
-    After filing an issue, use `-Zsuppress-const-validation-back-compat-ice` to compile your code.";
index 4adad16a5701068c6bb558a5a780ada45b48324d..1a4fc72e8178647074a35c253deb89a1bf47baca 100644 (file)
@@ -9,6 +9,7 @@ const fn f(x: usize) -> usize {
         //~| ERROR E0017
         //~| ERROR E0080
         //~| ERROR E0744
+        //~| ERROR E0019
         sum += i;
     }
     sum
index 04eecb5687fd903608ff9a0694cc5da1b278791c..ee37aaa5e13b943c7b715e3d42f24c47e210454a 100644 (file)
@@ -11,4 +11,5 @@ fn main() {
     //~| ERROR `for` is not allowed in a `const`
     //~| ERROR references in constants may only refer to immutable values
     //~| ERROR evaluation of constant value failed
+    //~| ERROR constant contains unimplemented expression type
 }
index 8cf527f0429f025eb3b9dec86fe56f1916adfde9..b1c058eac9e4bfc213d0c38553bdb9c1dc64dfc8 100644 (file)
@@ -1,7 +1,10 @@
-// Test for absence of validation mismatch ICE in #65394
+// This test originated from #65394. We conservatively assume that `x` is still `LiveDrop` even
+// after it has been moved because a mutable reference to it exists at some point in the const body.
+//
+// We will likely have to change this behavior before we allow `&mut` in a `const`.
 
 const _: Vec<i32> = {
-    let mut x = Vec::<i32>::new();
+    let mut x = Vec::<i32>::new(); //~ ERROR destructors cannot be evaluated at compile-time
     let r = &mut x; //~ ERROR references in constants may only refer to immutable values
     let y = x;
     y
index 15df813836e5ba46201431ab6dd9d9293cf604e7..acf5cbaede66597b552d4df556447e32495be118 100644 (file)
@@ -1,11 +1,16 @@
 error[E0017]: references in constants may only refer to immutable values
-  --> $DIR/issue-65394.rs:5:13
+  --> $DIR/issue-65394.rs:8:13
    |
 LL |     let r = &mut x;
    |             ^^^^^^ constants require immutable values
 
-[ERROR rustc_mir::transform::qualify_consts] old validator: [($DIR/issue-65394.rs:5:13: 5:19, "MutBorrow(Mut { allow_two_phase_borrow: false })")]
-[ERROR rustc_mir::transform::qualify_consts] new validator: [($DIR/issue-65394.rs:5:13: 5:19, "MutBorrow(Mut { allow_two_phase_borrow: false })"), ($DIR/issue-65394.rs:4:9: 4:14, "LiveDrop")]
-error: aborting due to previous error
+error[E0493]: destructors cannot be evaluated at compile-time
+  --> $DIR/issue-65394.rs:7:9
+   |
+LL |     let mut x = Vec::<i32>::new();
+   |         ^^^^^ constants cannot evaluate destructors
+
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0017`.
+Some errors have detailed explanations: E0017, E0493.
+For more information about an error, try `rustc --explain E0017`.
index d7cdb0babc5c5827699a2576eda83d833a1b28f1..76d62f069f37890bb45a10a74c09bceb83ed5477 100644 (file)
@@ -1,6 +1,9 @@
-// compile-flags: -Zunleash-the-miri-inside-of-you
+// compile-flags: -Zunleash-the-miri-inside-of-you -Awarnings
 // run-pass
 
+// miri unleashed warnings are not useful and change frequently, so they are silenced above.
+#![feature(const_panic)]
+
 //! Make sure that we read and write enum discriminants correctly for corner cases caused
 //! by layout optimizations.
 
@@ -21,7 +24,7 @@ enum Foo {
     }
 
     let x = Foo::B;
-    match x { //~ WARNING skipping const checks
+    match x {
         Foo::B => 0,
         _ => panic!(),
     }
@@ -86,21 +89,17 @@ pub enum E2<X> {
     }
 
     if let E1::V2 { .. } = (E1::V1 { f: true }) {
-        //~^ WARNING skipping const checks
         unreachable!()
     }
     if let E1::V1 { .. } = (E1::V1 { f: true }) {
-        //~^ WARNING skipping const checks
     } else {
         unreachable!()
     }
 
     if let E2::V1 { .. } = E2::V3::<Infallible> {
-        //~^ WARNING skipping const checks
         unreachable!()
     }
     if let E2::V3 { .. } = E2::V3::<Infallible> {
-        //~^ WARNING skipping const checks
     } else {
         unreachable!()
     }
diff --git a/src/test/ui/consts/miri_unleashed/enum_discriminants.stderr b/src/test/ui/consts/miri_unleashed/enum_discriminants.stderr
deleted file mode 100644 (file)
index b7fce22..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-warning: skipping const checks
-  --> $DIR/enum_discriminants.rs:24:5
-   |
-LL | /     match x {
-LL | |         Foo::B => 0,
-LL | |         _ => panic!(),
-LL | |     }
-   | |_____^
-
-warning: skipping const checks
-  --> $DIR/enum_discriminants.rs:88:5
-   |
-LL | /     if let E1::V2 { .. } = (E1::V1 { f: true }) {
-LL | |
-LL | |         unreachable!()
-LL | |     }
-   | |_____^
-
-warning: skipping const checks
-  --> $DIR/enum_discriminants.rs:92:5
-   |
-LL | /     if let E1::V1 { .. } = (E1::V1 { f: true }) {
-LL | |
-LL | |     } else {
-LL | |         unreachable!()
-LL | |     }
-   | |_____^
-
-warning: skipping const checks
-  --> $DIR/enum_discriminants.rs:98:5
-   |
-LL | /     if let E2::V1 { .. } = E2::V3::<Infallible> {
-LL | |
-LL | |         unreachable!()
-LL | |     }
-   | |_____^
-
-warning: skipping const checks
-  --> $DIR/enum_discriminants.rs:102:5
-   |
-LL | /     if let E2::V3 { .. } = E2::V3::<Infallible> {
-LL | |
-LL | |     } else {
-LL | |         unreachable!()
-LL | |     }
-   | |_____^
-