about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-06-19 09:43:58 +0000
committerbors <bors@rust-lang.org>2019-06-19 09:43:58 +0000
commit9cb052acfb25c12d5e8960f9ea53b69a2f19b0e8 (patch)
tree982b766b91ad352b683db1af1e6799a7531b65be
parenta6cbf2d1344418cd2807cc5380ef1247647a1e12 (diff)
parentcd290c7ee9cc00413116f402823475ed5735293a (diff)
downloadrust-9cb052acfb25c12d5e8960f9ea53b69a2f19b0e8.tar.gz
rust-9cb052acfb25c12d5e8960f9ea53b69a2f19b0e8.zip
Auto merge of #58351 - oli-obk:double_check_const_eval, r=RalfJung
Refactor interning to properly mark memory as mutable or immutable

r? @RalfJung

This implementation is incomplete out of multiple reasons

* [ ] add `-Zunleash_the_miri_inside_of_you` tests
* [ ] report an error if there's an `UnsafeCell` behind a reference in a constant
* [ ] make validity checks actually test whether the mutability of their allocations match what they see in the type
-rw-r--r--src/librustc/mir/interpret/value.rs9
-rw-r--r--src/librustc/ty/structural_impls.rs2
-rw-r--r--src/librustc_codegen_llvm/common.rs7
-rw-r--r--src/librustc_codegen_llvm/consts.rs4
-rw-r--r--src/librustc_codegen_ssa/mir/operand.rs4
-rw-r--r--src/librustc_codegen_ssa/mir/place.rs4
-rw-r--r--src/librustc_codegen_ssa/traits/consts.rs1
-rw-r--r--src/librustc_mir/const_eval.rs103
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs17
-rw-r--r--src/librustc_mir/interpret/eval_context.rs4
-rw-r--r--src/librustc_mir/interpret/intern.rs333
-rw-r--r--src/librustc_mir/interpret/memory.rs84
-rw-r--r--src/librustc_mir/interpret/mod.rs3
-rw-r--r--src/librustc_mir/interpret/operand.rs35
-rw-r--r--src/librustc_mir/interpret/place.rs14
-rw-r--r--src/librustc_mir/interpret/validity.rs88
-rw-r--r--src/librustc_mir/interpret/visitor.rs1
-rw-r--r--src/librustc_mir/monomorphize/collector.rs2
-rw-r--r--src/librustc_mir/transform/const_prop.rs9
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs42
-rw-r--r--src/librustc_typeck/check/mod.rs2
-rw-r--r--src/test/ui/consts/miri_unleashed/mutable_references.rs35
-rw-r--r--src/test/ui/consts/miri_unleashed/mutable_references.stderr26
-rw-r--r--src/test/ui/consts/miri_unleashed/mutable_references_ice.rs28
-rw-r--r--src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr21
-rw-r--r--src/test/ui/consts/packed_pattern.rs19
-rw-r--r--src/test/ui/consts/packed_pattern2.rs27
-rw-r--r--src/test/ui/consts/static-raw-pointer-interning.rs15
-rw-r--r--src/test/ui/consts/static-raw-pointer-interning2.rs15
-rw-r--r--src/test/ui/consts/union_constant.rs1
30 files changed, 758 insertions, 197 deletions
diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs
index f7b3385668f..40e81119973 100644
--- a/src/librustc/mir/interpret/value.rs
+++ b/src/librustc/mir/interpret/value.rs
@@ -2,7 +2,7 @@ use std::fmt;
 use rustc_macros::HashStable;
 use rustc_apfloat::{Float, ieee::{Double, Single}};
 
-use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef};
+use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size, Align}, subst::SubstsRef};
 use crate::ty::PlaceholderConst;
 use crate::hir::def_id::DefId;
 
@@ -45,7 +45,12 @@ pub enum ConstValue<'tcx> {
 
     /// An allocation together with a pointer into the allocation.
     /// Invariant: the pointer's `AllocId` resolves to the allocation.
-    ByRef(Pointer, &'tcx Allocation),
+    /// The alignment exists to allow `const_field` to have `ByRef` access to nonprimitive fields
+    /// of `repr(packed)` structs. The alignment may be lower than the type of this constant.
+    /// This permits reads with lower alignment than what the type would normally require.
+    /// FIXME(RalfJ,oli-obk): The alignment checks are part of miri, but const eval doesn't really
+    /// need them. Disabling them may be too hard though.
+    ByRef(Pointer, Align, &'tcx Allocation),
 
     /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
     /// variants when the code is monomorphic enough for that.
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index a4efb566e13..4cd0fd3e824 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -1335,7 +1335,7 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> {
 impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> {
     fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
         match *self {
-            ConstValue::ByRef(ptr, alloc) => ConstValue::ByRef(ptr, alloc),
+            ConstValue::ByRef(ptr, align, alloc) => ConstValue::ByRef(ptr, align, alloc),
             ConstValue::Infer(ic) => ConstValue::Infer(ic.fold_with(folder)),
             ConstValue::Param(p) => ConstValue::Param(p.fold_with(folder)),
             ConstValue::Placeholder(p) => ConstValue::Placeholder(p),
diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs
index 0b23aac5224..f21f203fcc9 100644
--- a/src/librustc_codegen_llvm/common.rs
+++ b/src/librustc_codegen_llvm/common.rs
@@ -11,7 +11,7 @@ use crate::value::Value;
 use rustc_codegen_ssa::traits::*;
 
 use crate::consts::const_alloc_to_llvm;
-use rustc::ty::layout::{HasDataLayout, LayoutOf, self, TyLayout, Size};
+use rustc::ty::layout::{HasDataLayout, LayoutOf, self, TyLayout, Size, Align};
 use rustc::mir::interpret::{Scalar, GlobalAlloc, Allocation};
 use rustc_codegen_ssa::mir::place::PlaceRef;
 
@@ -344,11 +344,12 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn from_const_alloc(
         &self,
         layout: TyLayout<'tcx>,
+        align: Align,
         alloc: &Allocation,
         offset: Size,
     ) -> PlaceRef<'tcx, &'ll Value> {
         let init = const_alloc_to_llvm(self, alloc);
-        let base_addr = self.static_addr_of(init, layout.align.abi, None);
+        let base_addr = self.static_addr_of(init, align, None);
 
         let llval = unsafe { llvm::LLVMConstInBoundsGEP(
             self.const_bitcast(base_addr, self.type_i8p()),
@@ -356,7 +357,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
             1,
         )};
         let llval = self.const_bitcast(llval, self.type_ptr_to(layout.llvm_type(self)));
-        PlaceRef::new_sized(llval, layout, alloc.align)
+        PlaceRef::new_sized(llval, layout, align)
     }
 
     fn const_ptrcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs
index eb059ac453c..4bf91bbed60 100644
--- a/src/librustc_codegen_llvm/consts.rs
+++ b/src/librustc_codegen_llvm/consts.rs
@@ -71,7 +71,9 @@ pub fn codegen_static_initializer(
     let static_ = cx.tcx.const_eval(param_env.and(cid))?;
 
     let alloc = match static_.val {
-        ConstValue::ByRef(ptr, alloc) if ptr.offset.bytes() == 0 => alloc,
+        ConstValue::ByRef(ptr, align, alloc) if ptr.offset.bytes() == 0 && align == alloc.align => {
+            alloc
+        },
         _ => bug!("static const eval returned {:#?}", static_),
     };
     Ok((const_alloc_to_llvm(cx, alloc), alloc))
diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs
index 3305dfe1ffb..c1626d31c78 100644
--- a/src/librustc_codegen_ssa/mir/operand.rs
+++ b/src/librustc_codegen_ssa/mir/operand.rs
@@ -109,8 +109,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
                 let b_llval = bx.const_usize((end - start) as u64);
                 OperandValue::Pair(a_llval, b_llval)
             },
-            ConstValue::ByRef(ptr, alloc) => {
-                return bx.load_operand(bx.from_const_alloc(layout, alloc, ptr.offset));
+            ConstValue::ByRef(ptr, align, alloc) => {
+                return bx.load_operand(bx.from_const_alloc(layout, align, alloc, ptr.offset));
             },
         };
 
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs
index 81b17d0bee8..72aedb4812a 100644
--- a/src/librustc_codegen_ssa/mir/place.rs
+++ b/src/librustc_codegen_ssa/mir/place.rs
@@ -424,8 +424,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let layout = cx.layout_of(self.monomorphize(&ty));
                 match bx.tcx().const_eval(param_env.and(cid)) {
                     Ok(val) => match val.val {
-                        mir::interpret::ConstValue::ByRef(ptr, alloc) => {
-                            bx.cx().from_const_alloc(layout, alloc, ptr.offset)
+                        mir::interpret::ConstValue::ByRef(ptr, align, alloc) => {
+                            bx.cx().from_const_alloc(layout, align, alloc, ptr.offset)
                         }
                         _ => bug!("promoteds should have an allocation: {:?}", val),
                     },
diff --git a/src/librustc_codegen_ssa/traits/consts.rs b/src/librustc_codegen_ssa/traits/consts.rs
index 32412f303c1..46286b5329e 100644
--- a/src/librustc_codegen_ssa/traits/consts.rs
+++ b/src/librustc_codegen_ssa/traits/consts.rs
@@ -34,6 +34,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
     fn from_const_alloc(
         &self,
         layout: layout::TyLayout<'tcx>,
+        align: layout::Align,
         alloc: &Allocation,
         offset: layout::Size,
     ) -> PlaceRef<'tcx, Self::Value>;
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 7d05e7be26e..284a8f40e1f 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -9,7 +9,7 @@ use std::convert::TryInto;
 
 use rustc::hir::def::DefKind;
 use rustc::hir::def_id::DefId;
-use rustc::mir::interpret::{ConstEvalErr, ErrorHandled};
+use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef};
 use rustc::mir;
 use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
 use rustc::ty::layout::{self, LayoutOf, VariantIdx};
@@ -18,15 +18,14 @@ use rustc::traits::Reveal;
 use rustc::util::common::ErrorReported;
 use rustc_data_structures::fx::FxHashMap;
 
-use syntax::ast::Mutability;
 use syntax::source_map::{Span, DUMMY_SP};
 
 use crate::interpret::{self,
-    PlaceTy, MPlaceTy, MemPlace, OpTy, ImmTy, Immediate, Scalar,
+    PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar,
     RawConst, ConstValue,
     InterpResult, InterpErrorInfo, InterpError, GlobalId, InterpretCx, StackPopCleanup,
     Allocation, AllocId, MemoryKind,
-    snapshot, RefTracking,
+    snapshot, RefTracking, intern_const_alloc_recursive,
 };
 
 /// Number of steps until the detector even starts doing anything.
@@ -63,33 +62,19 @@ pub(crate) fn eval_promoted<'mir, 'tcx>(
     eval_body_using_ecx(&mut ecx, cid, body, param_env)
 }
 
-fn mplace_to_const<'tcx>(
-    ecx: &CompileTimeEvalContext<'_, 'tcx>,
-    mplace: MPlaceTy<'tcx>,
-) -> &'tcx ty::Const<'tcx> {
-    let MemPlace { ptr, align, meta } = *mplace;
-    // extract alloc-offset pair
-    assert!(meta.is_none());
-    let ptr = ptr.to_ptr().unwrap();
-    let alloc = ecx.memory.get(ptr.alloc_id).unwrap();
-    assert!(alloc.align >= align);
-    assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= mplace.layout.size.bytes());
-    let mut alloc = alloc.clone();
-    alloc.align = align;
-    // FIXME shouldn't it be the case that `mark_static_initialized` has already
-    // interned this?  I thought that is the entire point of that `FinishStatic` stuff?
-    let alloc = ecx.tcx.intern_const_alloc(alloc);
-    let val = ConstValue::ByRef(ptr, alloc);
-    ecx.tcx.mk_const(ty::Const { val, ty: mplace.layout.ty })
-}
-
 fn op_to_const<'tcx>(
     ecx: &CompileTimeEvalContext<'_, 'tcx>,
     op: OpTy<'tcx>,
 ) -> &'tcx ty::Const<'tcx> {
-    // We do not normalize just any data.  Only non-union scalars and slices.
-    let normalize = match op.layout.abi {
-        layout::Abi::Scalar(..) => op.layout.ty.ty_adt_def().map_or(true, |adt| !adt.is_union()),
+    // We do not have value optmizations for everything.
+    // Only scalars and slices, since they are very common.
+    // Note that further down we turn scalars of undefined bits back to `ByRef`. These can result
+    // from scalar unions that are initialized with one of their zero sized variants. We could
+    // instead allow `ConstValue::Scalar` to store `ScalarMaybeUndef`, but that would affect all
+    // the usual cases of extracting e.g. a `usize`, without there being a real use case for the
+    // `Undef` situation.
+    let try_as_immediate = match op.layout.abi {
+        layout::Abi::Scalar(..) => true,
         layout::Abi::ScalarPair(..) => match op.layout.ty.sty {
             ty::Ref(_, inner, _) => match inner.sty {
                 ty::Slice(elem) => elem == ecx.tcx.types.u8,
@@ -100,16 +85,38 @@ fn op_to_const<'tcx>(
         },
         _ => false,
     };
-    let normalized_op = if normalize {
-        Err(*ecx.read_immediate(op).expect("normalization works on validated constants"))
+    let immediate = if try_as_immediate {
+        Err(ecx.read_immediate(op).expect("normalization works on validated constants"))
     } else {
+        // It is guaranteed that any non-slice scalar pair is actually ByRef here.
+        // When we come back from raw const eval, we are always by-ref. The only way our op here is
+        // by-val is if we are in const_field, i.e., if this is (a field of) something that we
+        // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
+        // structs containing such.
         op.try_as_mplace()
     };
-    let val = match normalized_op {
-        Ok(mplace) => return mplace_to_const(ecx, mplace),
-        Err(Immediate::Scalar(x)) =>
-            ConstValue::Scalar(x.not_undef().unwrap()),
-        Err(Immediate::ScalarPair(a, b)) => {
+    let val = match immediate {
+        Ok(mplace) => {
+            let ptr = mplace.ptr.to_ptr().unwrap();
+            let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
+            ConstValue::ByRef(ptr, mplace.align, alloc)
+        },
+        // see comment on `let try_as_immediate` above
+        Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x {
+            ScalarMaybeUndef::Scalar(s) => ConstValue::Scalar(s),
+            ScalarMaybeUndef::Undef => {
+                // When coming out of "normal CTFE", we'll always have an `Indirect` operand as
+                // argument and we will not need this. The only way we can already have an
+                // `Immediate` is when we are called from `const_field`, and that `Immediate`
+                // comes from a constant so it can happen have `Undef`, because the indirect
+                // memory that was read had undefined bytes.
+                let mplace = op.to_mem_place();
+                let ptr = mplace.ptr.to_ptr().unwrap();
+                let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
+                ConstValue::ByRef(ptr, mplace.align, alloc)
+            },
+        },
+        Err(ImmTy { imm: Immediate::ScalarPair(a, b), .. }) => {
             let (data, start) = match a.not_undef().unwrap() {
                 Scalar::Ptr(ptr) => (
                     ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
@@ -164,13 +171,12 @@ fn eval_body_using_ecx<'mir, 'tcx>(
     ecx.run()?;
 
     // Intern the result
-    let mutability = if tcx.is_mutable_static(cid.instance.def_id()) ||
-                     !layout.ty.is_freeze(tcx, param_env, body.span) {
-        Mutability::Mutable
-    } else {
-        Mutability::Immutable
-    };
-    ecx.memory.intern_static(ret.ptr.to_ptr()?.alloc_id, mutability)?;
+    intern_const_alloc_recursive(
+        ecx,
+        cid.instance.def_id(),
+        ret,
+        param_env,
+    )?;
 
     debug!("eval_body_using_ecx done: {:?}", *ret);
     Ok(ret)
@@ -297,7 +303,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
     }
 }
 
-type CompileTimeEvalContext<'mir, 'tcx> =
+crate type CompileTimeEvalContext<'mir, 'tcx> =
     InterpretCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>;
 
 impl interpret::MayLeak for ! {
@@ -526,13 +532,22 @@ fn validate_and_turn_into_const<'tcx>(
                 mplace.into(),
                 path,
                 Some(&mut ref_tracking),
-                true, // const mode
             )?;
         }
         // Now that we validated, turn this into a proper constant.
+        // Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
+        // whether they become immediates.
         let def_id = cid.instance.def.def_id();
         if tcx.is_static(def_id) || cid.promoted.is_some() {
-            Ok(mplace_to_const(&ecx, mplace))
+            let ptr = mplace.ptr.to_ptr()?;
+            Ok(tcx.mk_const(ty::Const {
+                val: ConstValue::ByRef(
+                    ptr,
+                    mplace.align,
+                    ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
+                ),
+                ty: mplace.layout.ty,
+            }))
         } else {
             Ok(op_to_const(&ecx, mplace.into()))
         }
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index 764ff0e15d5..974c863792e 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -215,10 +215,15 @@ impl LiteralExpander<'tcx> {
         debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
         match (val, &crty.sty, &rty.sty) {
             // the easy case, deref a reference
-            (ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => ConstValue::ByRef(
-                p,
-                self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id),
-            ),
+            (ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => {
+                let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
+                ConstValue::ByRef(
+                    p,
+                    // FIXME(oli-obk): this should be the type's layout
+                    alloc.align,
+                    alloc,
+                )
+            },
             // unsize array to slice if pattern is array but match value or other patterns are slice
             (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
                 assert_eq!(t, u);
@@ -1431,7 +1436,7 @@ fn slice_pat_covered_by_const<'tcx>(
     suffix: &[Pattern<'tcx>],
 ) -> Result<bool, ErrorReported> {
     let data: &[u8] = match (const_val.val, &const_val.ty.sty) {
-        (ConstValue::ByRef(ptr, alloc), ty::Array(t, n)) => {
+        (ConstValue::ByRef(ptr, _, alloc), ty::Array(t, n)) => {
             assert_eq!(*t, tcx.types.u8);
             let n = n.assert_usize(tcx).unwrap();
             alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
@@ -1753,7 +1758,7 @@ fn specialize<'p, 'a: 'p, 'tcx>(
                     let (alloc, offset, n, ty) = match value.ty.sty {
                         ty::Array(t, n) => {
                             match value.val {
-                                ConstValue::ByRef(ptr, alloc) => (
+                                ConstValue::ByRef(ptr, _, alloc) => (
                                     alloc,
                                     ptr.offset,
                                     n.unwrap_usize(cx.tcx),
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index b01826c98db..4afa4a0cbb3 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -576,7 +576,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
                     self.place_to_op(return_place)?,
                     vec![],
                     None,
-                    /*const_mode*/false,
                 )?;
             }
         } else {
@@ -641,6 +640,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
         &self,
         gid: GlobalId<'tcx>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        // FIXME(oli-obk): make this check an assertion that it's not a static here
+        // FIXME(RalfJ, oli-obk): document that `Place::Static` can never be anything but a static
+        // and `ConstValue::Unevaluated` can never be a static
         let param_env = if self.tcx.is_static(gid.instance.def_id()) {
             ty::ParamEnv::reveal_all()
         } else {
diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs
new file mode 100644
index 00000000000..d998f40c86e
--- /dev/null
+++ b/src/librustc_mir/interpret/intern.rs
@@ -0,0 +1,333 @@
+//! This module specifies the type based interner for constants.
+//!
+//! After a const evaluation has computed a value, before we destroy the const evaluator's session
+//! memory, we need to extract all memory allocations to the global memory pool so they stay around.
+
+use rustc::ty::layout::LayoutOf;
+use rustc::ty::{Ty, TyCtxt, ParamEnv, self};
+use rustc::mir::interpret::{
+    InterpResult, ErrorHandled,
+};
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use super::validity::RefTracking;
+use rustc_data_structures::fx::FxHashSet;
+
+use syntax::ast::Mutability;
+use syntax_pos::Span;
+
+use super::{
+    ValueVisitor, MemoryKind, Pointer, AllocId, MPlaceTy, InterpError, Scalar,
+};
+use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};
+
+struct InternVisitor<'rt, 'mir: 'rt, 'tcx: 'rt + 'mir> {
+    /// previously encountered safe references
+    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
+    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
+    param_env: ParamEnv<'tcx>,
+    /// The root node of the value that we're looking at. This field is never mutated and only used
+    /// for sanity assertions that will ICE when `const_qualif` screws up.
+    mode: InternMode,
+    /// This field stores the mutability of the value *currently* being checked.
+    /// It is set to mutable when an `UnsafeCell` is encountered
+    /// When recursing across a reference, we don't recurse but store the
+    /// value to be checked in `ref_tracking` together with the mutability at which we are checking
+    /// the value.
+    /// When encountering an immutable reference, we treat everything as immutable that is behind
+    /// it.
+    mutability: Mutability,
+    /// A list of all encountered relocations. After type-based interning, we traverse this list to
+    /// also intern allocations that are only referenced by a raw pointer or inside a union.
+    leftover_relocations: &'rt mut FxHashSet<AllocId>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
+enum InternMode {
+    /// Mutable references must in fact be immutable due to their surrounding immutability in a
+    /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
+    /// that will actually be treated as mutable.
+    Static,
+    /// UnsafeCell is OK in the value of a constant, but not behind references in a constant
+    ConstBase,
+    /// `UnsafeCell` ICEs
+    Const,
+}
+
+/// Signalling data structure to ensure we don't recurse
+/// into the memory of other constants or statics
+struct IsStaticOrFn;
+
+impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
+    /// Intern an allocation without looking at its children
+    fn intern_shallow(
+        &mut self,
+        ptr: Pointer,
+        mutability: Mutability,
+    ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
+        trace!(
+            "InternVisitor::intern {:?} with {:?}",
+            ptr, mutability,
+        );
+        // remove allocation
+        let tcx = self.ecx.tcx;
+        let memory = self.ecx.memory_mut();
+        let (kind, mut alloc) = match memory.alloc_map.remove(&ptr.alloc_id) {
+            Some(entry) => entry,
+            None => {
+                // if the pointer is dangling (neither in local nor global memory), we leave it
+                // to validation to error. The `delay_span_bug` ensures that we don't forget such
+                // a check in validation.
+                if tcx.alloc_map.lock().get(ptr.alloc_id).is_none() {
+                    tcx.sess.delay_span_bug(self.ecx.tcx.span, "tried to intern dangling pointer");
+                }
+                // treat dangling pointers like other statics
+                // just to stop trying to recurse into them
+                return Ok(Some(IsStaticOrFn));
+            },
+        };
+        // This match is just a canary for future changes to `MemoryKind`, which most likely need
+        // changes in this function.
+        match kind {
+            MemoryKind::Stack | MemoryKind::Vtable => {},
+        }
+        // Ensure llvm knows to only put this into immutable memory if the value is immutable either
+        // by being behind a reference or by being part of a static or const without interior
+        // mutability
+        alloc.mutability = mutability;
+        // link the alloc id to the actual allocation
+        let alloc = tcx.intern_const_alloc(alloc);
+        self.leftover_relocations.extend(alloc.relocations.iter().map(|&(_, ((), reloc))| reloc));
+        tcx.alloc_map.lock().set_alloc_id_memory(ptr.alloc_id, alloc);
+        Ok(None)
+    }
+}
+
+impl<'rt, 'mir, 'tcx>
+    ValueVisitor<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
+for
+    InternVisitor<'rt, 'mir, 'tcx>
+{
+    type V = MPlaceTy<'tcx>;
+
+    #[inline(always)]
+    fn ecx(&self) -> &CompileTimeEvalContext<'mir, 'tcx> {
+        &self.ecx
+    }
+
+    fn visit_aggregate(
+        &mut self,
+        mplace: MPlaceTy<'tcx>,
+        fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
+    ) -> InterpResult<'tcx> {
+        if let Some(def) = mplace.layout.ty.ty_adt_def() {
+            if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
+                // We are crossing over an `UnsafeCell`, we can mutate again
+                let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
+                assert_ne!(
+                    self.mode, InternMode::Const,
+                    "UnsafeCells are not allowed behind references in constants. This should have \
+                    been prevented statically by const qualification. If this were allowed one \
+                    would be able to change a constant at one use site and other use sites may \
+                    arbitrarily decide to change, too.",
+                );
+                let walked = self.walk_aggregate(mplace, fields);
+                self.mutability = old;
+                return walked;
+            }
+        }
+        self.walk_aggregate(mplace, fields)
+    }
+
+    fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
+        // Handle Reference types, as these are the only relocations supported by const eval.
+        // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
+        let ty = mplace.layout.ty;
+        if let ty::Ref(_, _, mutability) = ty.sty {
+            let value = self.ecx.read_immediate(mplace.into())?;
+            // Handle trait object vtables
+            if let Ok(meta) = value.to_meta() {
+                let layout = self.ecx.layout_of(ty.builtin_deref(true).unwrap().ty)?;
+                if layout.is_unsized() {
+                    if let ty::Dynamic(..) = self.ecx.tcx.struct_tail(layout.ty).sty {
+                        if let Ok(vtable) = meta.unwrap().to_ptr() {
+                            // explitly choose `Immutable` here, since vtables are immutable, even
+                            // if the reference of the fat pointer is mutable
+                            self.intern_shallow(vtable, Mutability::Immutable)?;
+                        }
+                    }
+                }
+            }
+            let mplace = self.ecx.ref_to_mplace(value)?;
+            // Check if we have encountered this pointer+layout combination before.
+            // Only recurse for allocation-backed pointers.
+            if let Scalar::Ptr(ptr) = mplace.ptr {
+                // We do not have any `frozen` logic here, because it's essentially equivalent to
+                // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
+                // and we check that in `visit_aggregate`.
+                // This is not an inherent limitation, but one that we know to be true, because
+                // const qualification enforces it. We can lift it in the future.
+                match (self.mode, mutability) {
+                    // immutable references are fine everywhere
+                    (_, hir::Mutability::MutImmutable) => {},
+                    // all is "good and well" in the unsoundness of `static mut`
+
+                    // mutable references are ok in `static`. Either they are treated as immutable
+                    // because they are behind an immutable one, or they are behind an `UnsafeCell`
+                    // and thus ok.
+                    (InternMode::Static, hir::Mutability::MutMutable) => {},
+                    // we statically prevent `&mut T` via `const_qualif` and double check this here
+                    (InternMode::ConstBase, hir::Mutability::MutMutable) |
+                    (InternMode::Const, hir::Mutability::MutMutable) =>
+                        bug!("const qualif failed to prevent mutable references"),
+                }
+                // Compute the mutability with which we'll start visiting the allocation. This is
+                // what gets changed when we encounter an `UnsafeCell`
+                let mutability = match (self.mutability, mutability) {
+                    // The only way a mutable reference actually works as a mutable reference is
+                    // by being in a `static mut` directly or behind another mutable reference.
+                    // If there's an immutable reference or we are inside a static, then our
+                    // mutable reference is equivalent to an immutable one. As an example:
+                    // `&&mut Foo` is semantically equivalent to `&&Foo`
+                    (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
+                    _ => Mutability::Immutable,
+                };
+                // Compute the mutability of the allocation
+                let intern_mutability = intern_mutability(
+                    self.ecx.tcx.tcx,
+                    self.param_env,
+                    mplace.layout.ty,
+                    self.ecx.tcx.span,
+                    mutability,
+                );
+                // Recursing behind references changes the intern mode for constants in order to
+                // cause assertions to trigger if we encounter any `UnsafeCell`s.
+                let mode = match self.mode {
+                    InternMode::ConstBase => InternMode::Const,
+                    other => other,
+                };
+                match self.intern_shallow(ptr, intern_mutability)? {
+                    // No need to recurse, these are interned already and statics may have
+                    // cycles, so we don't want to recurse there
+                    Some(IsStaticOrFn) => {},
+                    // intern everything referenced by this value. The mutability is taken from the
+                    // reference. It is checked above that mutable references only happen in
+                    // `static mut`
+                    None => self.ref_tracking.track((mplace, mutability, mode), || ()),
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+/// Figure out the mutability of the allocation.
+/// Mutable if it has interior mutability *anywhere* in the type.
+fn intern_mutability<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+    span: Span,
+    mutability: Mutability,
+) -> Mutability {
+    let has_interior_mutability = !ty.is_freeze(tcx, param_env, span);
+    if has_interior_mutability {
+        Mutability::Mutable
+    } else {
+        mutability
+    }
+}
+
+pub fn intern_const_alloc_recursive(
+    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
+    def_id: DefId,
+    ret: MPlaceTy<'tcx>,
+    // FIXME(oli-obk): can we scrap the param env? I think we can, the final value of a const eval
+    // must always be monomorphic, right?
+    param_env: ty::ParamEnv<'tcx>,
+) -> InterpResult<'tcx> {
+    let tcx = ecx.tcx;
+    // this `mutability` is the mutability of the place, ignoring the type
+    let (mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
+        Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
+        None => (Mutability::Immutable, InternMode::ConstBase),
+        // `static mut` doesn't care about interior mutability, it's mutable anyway
+        Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
+    };
+
+    // type based interning
+    let mut ref_tracking = RefTracking::new((ret, mutability, base_intern_mode));
+    let leftover_relocations = &mut FxHashSet::default();
+
+    // This mutability is the combination of the place mutability and the type mutability. If either
+    // is mutable, `alloc_mutability` is mutable. This exists because the entire allocation needs
+    // to be mutable if it contains an `UnsafeCell` anywhere. The other `mutability` exists so that
+    // the visitor does not treat everything outside the `UnsafeCell` as mutable.
+    let alloc_mutability = intern_mutability(
+        tcx.tcx, param_env, ret.layout.ty, tcx.span, mutability,
+    );
+
+    // start with the outermost allocation
+    InternVisitor {
+        ref_tracking: &mut ref_tracking,
+        ecx,
+        mode: base_intern_mode,
+        leftover_relocations,
+        param_env,
+        mutability,
+    }.intern_shallow(ret.ptr.to_ptr()?, alloc_mutability)?;
+
+    while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
+        let interned = InternVisitor {
+            ref_tracking: &mut ref_tracking,
+            ecx,
+            mode,
+            leftover_relocations,
+            param_env,
+            mutability,
+        }.visit_value(mplace);
+        if let Err(error) = interned {
+            // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
+            // to read enum discriminants in order to find references in enum variant fields.
+            if let InterpError::ValidationFailure(_) = error.kind {
+                let err = crate::const_eval::error_to_const_error(&ecx, error);
+                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
+                    Ok(mut diag) => {
+                        diag.note("The rules on what exactly is undefined behavior aren't clear, \
+                            so this check might be overzealous. Please open an issue on the rust \
+                            compiler repository if you believe it should not be considered \
+                            undefined behavior",
+                        );
+                        diag.emit();
+                    }
+                    Err(ErrorHandled::TooGeneric) |
+                    Err(ErrorHandled::Reported) => {},
+                }
+            }
+        }
+    }
+
+    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
+    // pointers, ... So we can't intern them according to their type rules
+
+    let mut todo: Vec<_> = leftover_relocations.iter().cloned().collect();
+    while let Some(alloc_id) = todo.pop() {
+        if let Some((_, alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
+            // We can't call the `intern` method here, as its logic is tailored to safe references.
+            // So we hand-roll the interning logic here again
+            let alloc = tcx.intern_const_alloc(alloc);
+            tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
+            for &(_, ((), reloc)) in alloc.relocations.iter() {
+                if leftover_relocations.insert(reloc) {
+                    todo.push(reloc);
+                }
+            }
+        } else if ecx.memory().dead_alloc_map.contains_key(&alloc_id) {
+            // dangling pointer
+            return err!(ValidationFailure(
+                "encountered dangling pointer in final constant".into(),
+            ))
+        }
+    }
+    Ok(())
+}
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index a0a34df3a5e..f2a115f8f35 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -20,6 +20,7 @@ use super::{
     Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
     InterpResult, Scalar, InterpError, GlobalAlloc, PointerArithmetic,
     Machine, AllocMap, MayLeak, ErrorHandled, CheckInAllocMsg, InboundsCheck,
+    InterpError::ValidationFailure,
 };
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
@@ -55,12 +56,13 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
     /// the wrong type), so we let the machine override this type.
     /// Either way, if the machine allows writing to a static, doing so will
     /// create a copy of the static allocation here.
-    alloc_map: M::MemoryMap,
+    // FIXME: this should not be public, but interning currently needs access to it
+    pub(super) alloc_map: M::MemoryMap,
 
     /// To be able to compare pointers with NULL, and to check alignment for accesses
     /// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
     /// that do not exist any more.
-    dead_alloc_map: FxHashMap<AllocId, (Size, Align)>,
+    pub(super) dead_alloc_map: FxHashMap<AllocId, (Size, Align)>,
 
     /// Extra data added by the machine.
     pub extra: M::MemoryExtra,
@@ -452,9 +454,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         if let Ok(alloc) = self.get(id) {
             return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
         }
+        // can't do this in the match argument, we may get cycle errors since the lock would get
+        // dropped after the match.
+        let alloc = self.tcx.alloc_map.lock().get(id);
         // Could also be a fn ptr or extern static
-        match self.tcx.alloc_map.lock().get(id) {
+        match alloc {
             Some(GlobalAlloc::Function(..)) => Ok((Size::ZERO, Align::from_bytes(1).unwrap())),
+            // `self.get` would also work, but can cause cycles if a static refers to itself
             Some(GlobalAlloc::Static(did)) => {
                 // The only way `get` couldn't have worked here is if this is an extern static
                 assert!(self.tcx.is_foreign_item(did));
@@ -463,14 +469,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                 let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
                 Ok((layout.size, layout.align.abi))
             }
-            _ => match liveness {
-                InboundsCheck::MaybeDead => {
-                    // Must be a deallocated pointer
-                    Ok(*self.dead_alloc_map.get(&id).expect(
-                        "allocation missing in dead_alloc_map"
-                    ))
-                },
-                InboundsCheck::Live => err!(DanglingPointerDeref),
+            _ => {
+                if let Ok(alloc) = self.get(id) {
+                    return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
+                }
+                match liveness {
+                    InboundsCheck::MaybeDead => {
+                        // Must be a deallocated pointer
+                        self.dead_alloc_map.get(&id).cloned().ok_or_else(||
+                            ValidationFailure("allocation missing in dead_alloc_map".to_string())
+                                .into()
+                        )
+                    },
+                    InboundsCheck::Live => err!(DanglingPointerDeref),
+                }
             },
         }
     }
@@ -633,56 +645,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     }
 }
 
-/// Interning (for CTFE)
-impl<'mir, 'tcx, M> Memory<'mir, 'tcx, M>
-where
-    M: Machine<'mir, 'tcx, PointerTag = (), AllocExtra = (), MemoryExtra = ()>,
-    // FIXME: Working around https://github.com/rust-lang/rust/issues/24159
-    M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>,
-{
-    /// mark an allocation as static and initialized, either mutable or not
-    pub fn intern_static(
-        &mut self,
-        alloc_id: AllocId,
-        mutability: Mutability,
-    ) -> InterpResult<'tcx> {
-        trace!(
-            "mark_static_initialized {:?}, mutability: {:?}",
-            alloc_id,
-            mutability
-        );
-        // remove allocation
-        let (kind, mut alloc) = self.alloc_map.remove(&alloc_id).unwrap();
-        match kind {
-            MemoryKind::Machine(_) => bug!("Static cannot refer to machine memory"),
-            MemoryKind::Stack | MemoryKind::Vtable => {},
-        }
-        // ensure llvm knows not to put this into immutable memory
-        alloc.mutability = mutability;
-        let alloc = self.tcx.intern_const_alloc(alloc);
-        self.tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
-        // recurse into inner allocations
-        for &(_, alloc) in alloc.relocations.values() {
-            // FIXME: Reusing the mutability here is likely incorrect.  It is originally
-            // determined via `is_freeze`, and data is considered frozen if there is no
-            // `UnsafeCell` *immediately* in that data -- however, this search stops
-            // at references.  So whenever we follow a reference, we should likely
-            // assume immutability -- and we should make sure that the compiler
-            // does not permit code that would break this!
-            if self.alloc_map.contains_key(&alloc) {
-                // Not yet interned, so proceed recursively
-                self.intern_static(alloc, mutability)?;
-            } else if self.dead_alloc_map.contains_key(&alloc) {
-                // dangling pointer
-                return err!(ValidationFailure(
-                    "encountered dangling pointer in final constant".into(),
-                ))
-            }
-        }
-        Ok(())
-    }
-}
-
 /// Reading and writing.
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     pub fn copy(
diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs
index d59bee6943a..0293a8366d0 100644
--- a/src/librustc_mir/interpret/mod.rs
+++ b/src/librustc_mir/interpret/mod.rs
@@ -14,6 +14,7 @@ mod traits;
 mod validity;
 mod intrinsics;
 mod visitor;
+mod intern;
 
 pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here
 
@@ -34,3 +35,5 @@ pub use self::visitor::{ValueVisitor, MutValueVisitor};
 pub use self::validity::RefTracking;
 
 pub(super) use self::intrinsics::type_name;
+
+pub use self::intern::intern_const_alloc_recursive;
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 4b1e782ba1a..1b451e0b8f1 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -4,7 +4,9 @@
 use std::convert::TryInto;
 
 use rustc::{mir, ty};
-use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx};
+use rustc::ty::layout::{
+    self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx,
+};
 
 use rustc::mir::interpret::{
     GlobalId, AllocId, CheckInAllocMsg,
@@ -217,7 +219,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
     fn try_read_immediate_from_mplace(
         &self,
         mplace: MPlaceTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, Option<Immediate<M::PointerTag>>> {
+    ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> {
         if mplace.layout.is_unsized() {
             // Don't touch unsized
             return Ok(None);
@@ -228,7 +230,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
             // Not all ZSTs have a layout we would handle below, so just short-circuit them
             // all here.
             self.memory.check_align(ptr, ptr_align)?;
-            return Ok(Some(Immediate::Scalar(Scalar::zst().into())));
+            return Ok(Some(ImmTy {
+                imm: Immediate::Scalar(Scalar::zst().into()),
+                layout: mplace.layout,
+            }));
         }
 
         // check for integer pointers before alignment to report better errors
@@ -239,7 +244,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
                 let scalar = self.memory
                     .get(ptr.alloc_id)?
                     .read_scalar(self, ptr, mplace.layout.size)?;
-                Ok(Some(Immediate::Scalar(scalar)))
+                Ok(Some(ImmTy {
+                    imm: Immediate::Scalar(scalar),
+                    layout: mplace.layout,
+                }))
             }
             layout::Abi::ScalarPair(ref a, ref b) => {
                 let (a, b) = (&a.value, &b.value);
@@ -256,7 +264,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
                 let b_val = self.memory
                     .get(ptr.alloc_id)?
                     .read_scalar(self, b_ptr, b_size)?;
-                Ok(Some(Immediate::ScalarPair(a_val, b_val)))
+                Ok(Some(ImmTy {
+                    imm: Immediate::ScalarPair(a_val, b_val),
+                    layout: mplace.layout,
+                }))
             }
             _ => Ok(None),
         }
@@ -271,13 +282,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
     pub(crate) fn try_read_immediate(
         &self,
         src: OpTy<'tcx, M::PointerTag>,
-    ) -> InterpResult<'tcx, Result<Immediate<M::PointerTag>, MemPlace<M::PointerTag>>> {
+    ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> {
         Ok(match src.try_as_mplace() {
             Ok(mplace) => {
                 if let Some(val) = self.try_read_immediate_from_mplace(mplace)? {
                     Ok(val)
                 } else {
-                    Err(*mplace)
+                    Err(mplace)
                 }
             },
             Err(val) => Ok(val),
@@ -291,7 +302,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
         op: OpTy<'tcx, M::PointerTag>
     ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
         if let Ok(imm) = self.try_read_immediate(op)? {
-            Ok(ImmTy { imm, layout: op.layout })
+            Ok(imm)
         } else {
             bug!("primitive read failed for type: {:?}", op.layout.ty);
         }
@@ -339,9 +350,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
             return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout });
         }
         let offset = op.layout.fields.offset(field);
-        let immediate = match base {
+        let immediate = match *base {
             // the field covers the entire type
-            _ if offset.bytes() == 0 && field_layout.size == op.layout.size => base,
+            _ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base,
             // extract fields from types with `ScalarPair` ABI
             Immediate::ScalarPair(a, b) => {
                 let val = if offset.bytes() == 0 { a } else { b };
@@ -527,11 +538,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
             self.layout_of(self.monomorphize(val.ty)?)
         })?;
         let op = match val.val {
-            ConstValue::ByRef(ptr, _alloc) => {
+            ConstValue::ByRef(ptr, align, _alloc) => {
                 // We rely on mutability being set correctly in that allocation to prevent writes
                 // where none should happen.
                 let ptr = self.tag_static_base_pointer(ptr);
-                Operand::Indirect(MemPlace::from_ptr(ptr, layout.align.abi))
+                Operand::Indirect(MemPlace::from_ptr(ptr, align))
             },
             ConstValue::Scalar(x) =>
                 Operand::Immediate(Immediate::Scalar(tag_scalar(x).into())),
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index a8f88af3f38..1285549015c 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -232,10 +232,10 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
 
 impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> {
     #[inline(always)]
-    pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, Immediate<Tag>> {
+    pub fn try_as_mplace(self) -> Result<MPlaceTy<'tcx, Tag>, ImmTy<'tcx, Tag>> {
         match *self {
             Operand::Indirect(mplace) => Ok(MPlaceTy { mplace, layout: self.layout }),
-            Operand::Immediate(imm) => Err(imm),
+            Operand::Immediate(imm) => Err(ImmTy { imm, layout: self.layout }),
         }
     }
 
@@ -660,7 +660,7 @@ where
 
         if M::enforce_validity(self) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(self.place_to_op(dest)?, vec![], None, /*const_mode*/false)?;
+            self.validate_operand(self.place_to_op(dest)?, vec![], None)?;
         }
 
         Ok(())
@@ -677,7 +677,7 @@ where
 
         if M::enforce_validity(self) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(dest.into(), vec![], None, /*const_mode*/ false)?;
+            self.validate_operand(dest.into(), vec![], None)?;
         }
 
         Ok(())
@@ -809,7 +809,7 @@ where
 
         if M::enforce_validity(self) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(self.place_to_op(dest)?, vec![], None, /*const_mode*/false)?;
+            self.validate_operand(self.place_to_op(dest)?, vec![], None)?;
         }
 
         Ok(())
@@ -836,7 +836,7 @@ where
                 // Yay, we got a value that we can write directly.
                 // FIXME: Add a check to make sure that if `src` is indirect,
                 // it does not overlap with `dest`.
-                return self.write_immediate_no_validate(src_val, dest);
+                return self.write_immediate_no_validate(*src_val, dest);
             }
             Err(mplace) => mplace,
         };
@@ -897,7 +897,7 @@ where
 
         if M::enforce_validity(self) {
             // Data got changed, better make sure it matches the type!
-            self.validate_operand(dest.into(), vec![], None, /*const_mode*/false)?;
+            self.validate_operand(dest.into(), vec![], None)?;
         }
 
         Ok(())
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index 3460c21b52d..d747eddd8d7 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -1,5 +1,4 @@
 use std::fmt::Write;
-use std::hash::Hash;
 use std::ops::RangeInclusive;
 
 use syntax_pos::symbol::{sym, Symbol};
@@ -11,6 +10,8 @@ use rustc::mir::interpret::{
     Scalar, GlobalAlloc, InterpResult, InterpError, CheckInAllocMsg,
 };
 
+use std::hash::Hash;
+
 use super::{
     OpTy, Machine, InterpretCx, ValueVisitor, MPlaceTy,
 };
@@ -76,19 +77,34 @@ pub enum PathElem {
 }
 
 /// State for tracking recursive validation of references
-pub struct RefTracking<T> {
+pub struct RefTracking<T, PATH = ()> {
     pub seen: FxHashSet<T>,
-    pub todo: Vec<(T, Vec<PathElem>)>,
+    pub todo: Vec<(T, PATH)>,
 }
 
-impl<T: Copy + Eq + Hash> RefTracking<T> {
+impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> {
+    pub fn empty() -> Self {
+        RefTracking {
+            seen: FxHashSet::default(),
+            todo: vec![],
+        }
+    }
     pub fn new(op: T) -> Self {
-        let mut ref_tracking = RefTracking {
+        let mut ref_tracking_for_consts = RefTracking {
             seen: FxHashSet::default(),
-            todo: vec![(op, Vec::new())],
+            todo: vec![(op, PATH::default())],
         };
-        ref_tracking.seen.insert(op);
-        ref_tracking
+        ref_tracking_for_consts.seen.insert(op);
+        ref_tracking_for_consts
+    }
+
+    pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) {
+        if self.seen.insert(op) {
+            trace!("Recursing below ptr {:#?}", op);
+            let path = path();
+            // Remember to come back to this later.
+            self.todo.push((op, path));
+        }
     }
 }
 
@@ -154,8 +170,10 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
     /// starts must not be changed!  `visit_fields` and `visit_array` rely on
     /// this stack discipline.
     path: Vec<PathElem>,
-    ref_tracking: Option<&'rt mut RefTracking<MPlaceTy<'tcx, M::PointerTag>>>,
-    const_mode: bool,
+    ref_tracking_for_consts: Option<&'rt mut RefTracking<
+        MPlaceTy<'tcx, M::PointerTag>,
+        Vec<PathElem>,
+    >>,
     ecx: &'rt InterpretCx<'mir, 'tcx, M>,
 }
 
@@ -314,7 +332,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 // types below!
                 let size = value.layout.size;
                 let value = value.to_scalar_or_undef();
-                if self.const_mode {
+                if self.ref_tracking_for_consts.is_some() {
                     // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
                     try_validation!(value.to_bits(size),
                         value, self.path, "initialized plain (non-pointer) bytes");
@@ -324,7 +342,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 }
             }
             ty::RawPtr(..) => {
-                if self.const_mode {
+                if self.ref_tracking_for_consts.is_some() {
                     // Integers/floats in CTFE: For consistency with integers, we do not
                     // accept undef.
                     let _ptr = try_validation!(value.to_scalar_ptr(),
@@ -393,9 +411,9 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                     }
                 }
                 // Recursive checking
-                if let Some(ref mut ref_tracking) = self.ref_tracking {
-                    assert!(self.const_mode, "We should only do recursie checking in const mode");
+                if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts {
                     let place = self.ecx.ref_to_mplace(value)?;
+                    // FIXME(RalfJ): check ZST for inbound pointers
                     if size != Size::ZERO {
                         // Non-ZST also have to be dereferencable
                         let ptr = try_validation!(place.ptr.to_ptr(),
@@ -423,16 +441,15 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                     // before.  Proceed recursively even for integer pointers, no
                     // reason to skip them! They are (recursively) valid for some ZST,
                     // but not for others (e.g., `!` is a ZST).
-                    if ref_tracking.seen.insert(place) {
-                        trace!("Recursing below ptr {:#?}", *place);
+                    let path = &self.path;
+                    ref_tracking.track(place, || {
                         // We need to clone the path anyway, make sure it gets created
                         // with enough space for the additional `Deref`.
-                        let mut new_path = Vec::with_capacity(self.path.len()+1);
-                        new_path.clone_from(&self.path);
+                        let mut new_path = Vec::with_capacity(path.len() + 1);
+                        new_path.clone_from(path);
                         new_path.push(PathElem::Deref);
-                        // Remember to come back to this later.
-                        ref_tracking.todo.push((place, new_path));
-                    }
+                        new_path
+                    });
                 }
             }
             ty::FnPtr(_sig) => {
@@ -488,10 +505,17 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                     let non_null =
                         self.ecx.memory.check_align(
                             Scalar::Ptr(ptr), Align::from_bytes(1).unwrap()
-                        ).is_ok() ||
-                        self.ecx.memory.get_fn(ptr).is_ok();
+                        ).is_ok();
                     if !non_null {
-                        // could be NULL
+                        // These conditions are just here to improve the diagnostics so we can
+                        // differentiate between null pointers and dangling pointers
+                        if self.ref_tracking_for_consts.is_some() &&
+                            self.ecx.memory.get(ptr.alloc_id).is_err() &&
+                            self.ecx.memory.get_fn(ptr).is_err() {
+                            return validation_failure!(
+                                "encountered dangling pointer", self.path
+                            );
+                        }
                         return validation_failure!("a potentially NULL pointer", self.path);
                     }
                     return Ok(());
@@ -574,7 +598,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                     self.ecx,
                     ptr,
                     size,
-                    /*allow_ptr_and_undef*/!self.const_mode,
+                    /*allow_ptr_and_undef*/ self.ref_tracking_for_consts.is_none(),
                 ) {
                     // In the happy case, we needn't check anything else.
                     Ok(()) => {},
@@ -612,23 +636,25 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
     /// is an indirect operand.
     /// It will error if the bits at the destination do not match the ones described by the layout.
     ///
-    /// `ref_tracking` can be `None` to avoid recursive checking below references.
+    /// `ref_tracking_for_consts` can be `None` to avoid recursive checking below references.
     /// This also toggles between "run-time" (no recursion) and "compile-time" (with recursion)
-    /// validation (e.g., pointer values are fine in integers at runtime).
+    /// validation (e.g., pointer values are fine in integers at runtime) and various other const
+    /// specific validation checks
     pub fn validate_operand(
         &self,
         op: OpTy<'tcx, M::PointerTag>,
         path: Vec<PathElem>,
-        ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::PointerTag>>>,
-        const_mode: bool,
+        ref_tracking_for_consts: Option<&mut RefTracking<
+            MPlaceTy<'tcx, M::PointerTag>,
+            Vec<PathElem>,
+        >>,
     ) -> InterpResult<'tcx> {
         trace!("validate_operand: {:?}, {:?}", *op, op.layout.ty);
 
         // Construct a visitor
         let mut visitor = ValidityVisitor {
             path,
-            ref_tracking,
-            const_mode,
+            ref_tracking_for_consts,
             ecx: self,
         };
 
diff --git a/src/librustc_mir/interpret/visitor.rs b/src/librustc_mir/interpret/visitor.rs
index 9150f16526b..d04dc3ab37e 100644
--- a/src/librustc_mir/interpret/visitor.rs
+++ b/src/librustc_mir/interpret/visitor.rs
@@ -81,6 +81,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::
         ecx.operand_field(self, field)
     }
 }
+
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for MPlaceTy<'tcx, M::PointerTag> {
     #[inline(always)]
     fn layout(&self) -> TyLayout<'tcx> {
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index acc786050a8..c64bb73802d 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -1262,7 +1262,7 @@ fn collect_const<'tcx>(
         ConstValue::Scalar(Scalar::Ptr(ptr)) =>
             collect_miri(tcx, ptr.alloc_id, output),
         ConstValue::Slice { data: alloc, start: _, end: _ } |
-        ConstValue::ByRef(_, alloc) => {
+        ConstValue::ByRef(_, _, alloc) => {
             for &((), id) in alloc.relocations.values() {
                 collect_miri(tcx, id, output);
             }
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 2ec5c192726..5b567512a7b 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -551,7 +551,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         source_info: SourceInfo,
     ) {
         trace!("attepting to replace {:?} with {:?}", rval, value);
-        if let Err(e) = self.ecx.validate_operand(value, vec![], None, true) {
+        if let Err(e) = self.ecx.validate_operand(
+            value,
+            vec![],
+            // FIXME: is ref tracking too expensive?
+            Some(&mut interpret::RefTracking::empty()),
+        ) {
             trace!("validation error, attempt failed: {:?}", e);
             return;
         }
@@ -562,7 +567,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         });
 
         if let Some(Ok(imm)) = imm {
-            match imm {
+            match *imm {
                 interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => {
                     *rval = Rvalue::Use(
                         self.operand_from_scalar(scalar, value.layout.ty, source_info.span));
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index b6abfdb7425..f082b5e5a04 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -738,27 +738,29 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                 qualifs[IsNotPromotable] = true;
 
                 if self.mode.requires_const_checking() {
-                    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.");
+                    if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
+                        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");
                         }
-                        err.emit();
-                    } else {
-                        span_err!(self.tcx.sess, self.span, E0492,
-                                  "cannot borrow a constant which may contain \
-                                   interior mutability, create a static instead");
                     }
                 }
             } else if let BorrowKind::Mut { .. } | BorrowKind::Shared = kind {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 177e2158044..fe742a74fff 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1448,7 +1448,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: DefId, span: Span)
     };
     let param_env = ty::ParamEnv::reveal_all();
     if let Ok(static_) = tcx.const_eval(param_env.and(cid)) {
-        let alloc = if let ConstValue::ByRef(_, allocation) = static_.val {
+        let alloc = if let ConstValue::ByRef(_, _, allocation) = static_.val {
             allocation
         } else {
             bug!("Matching on non-ByRef static")
diff --git a/src/test/ui/consts/miri_unleashed/mutable_references.rs b/src/test/ui/consts/miri_unleashed/mutable_references.rs
new file mode 100644
index 00000000000..5f9888053a1
--- /dev/null
+++ b/src/test/ui/consts/miri_unleashed/mutable_references.rs
@@ -0,0 +1,35 @@
+// compile-flags: -Zunleash-the-miri-inside-of-you
+#![allow(const_err)]
+
+use std::cell::UnsafeCell;
+
+// a test demonstrating what things we could allow with a smarter const qualification
+
+static FOO: &&mut u32 = &&mut 42;
+
+static BAR: &mut () = &mut ();
+
+struct Foo<T>(T);
+
+static BOO: &mut Foo<()> = &mut Foo(());
+
+struct Meh {
+    x: &'static UnsafeCell<i32>,
+}
+
+unsafe impl Sync for Meh {}
+
+static MEH: Meh = Meh {
+    x: &UnsafeCell::new(42),
+};
+
+static OH_YES: &mut i32 = &mut 42;
+
+fn main() {
+    unsafe {
+        *MEH.x.get() = 99; //~ WARN skipping const checks
+        //~^ WARN skipping const checks
+    }
+    *OH_YES = 99; //~ ERROR cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item
+    //~^ WARN skipping const checks
+}
diff --git a/src/test/ui/consts/miri_unleashed/mutable_references.stderr b/src/test/ui/consts/miri_unleashed/mutable_references.stderr
new file mode 100644
index 00000000000..b870aca640a
--- /dev/null
+++ b/src/test/ui/consts/miri_unleashed/mutable_references.stderr
@@ -0,0 +1,26 @@
+warning: skipping const checks
+  --> $DIR/mutable_references.rs:30:10
+   |
+LL |         *MEH.x.get() = 99;
+   |          ^^^^^
+
+warning: skipping const checks
+  --> $DIR/mutable_references.rs:30:9
+   |
+LL |         *MEH.x.get() = 99;
+   |         ^^^^^^^^^^^^^^^^^
+
+warning: skipping const checks
+  --> $DIR/mutable_references.rs:33:5
+   |
+LL |     *OH_YES = 99;
+   |     ^^^^^^^^^^^^
+
+error[E0594]: cannot assign to `*OH_YES`, as `OH_YES` is an immutable static item
+  --> $DIR/mutable_references.rs:33:5
+   |
+LL |     *OH_YES = 99;
+   |     ^^^^^^^^^^^^ cannot assign
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs b/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs
new file mode 100644
index 00000000000..4a77534c6c7
--- /dev/null
+++ b/src/test/ui/consts/miri_unleashed/mutable_references_ice.rs
@@ -0,0 +1,28 @@
+// compile-flags: -Zunleash-the-miri-inside-of-you
+// failure-status: 101
+// rustc-env:RUST_BACKTRACE=0
+// normalize-stderr-test "note: rustc 1.* running on .*" -> "note: rustc VERSION running on TARGET"
+// normalize-stderr-test "note: compiler flags: .*" -> "note: compiler flags: FLAGS"
+
+#![allow(const_err)]
+
+use std::cell::UnsafeCell;
+
+// this test ICEs to ensure that our mutability story is sound
+
+struct Meh {
+    x: &'static UnsafeCell<i32>,
+}
+
+unsafe impl Sync for Meh {}
+
+// the following will never be ok!
+const MUH: Meh = Meh {
+    x: &UnsafeCell::new(42),
+};
+
+fn main() {
+    unsafe {
+        *MUH.x.get() = 99; //~ WARN skipping const checks
+    }
+}
diff --git a/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr b/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr
new file mode 100644
index 00000000000..ff2351e1fdc
--- /dev/null
+++ b/src/test/ui/consts/miri_unleashed/mutable_references_ice.stderr
@@ -0,0 +1,21 @@
+warning: skipping const checks
+  --> $DIR/mutable_references_ice.rs:26:9
+   |
+LL |         *MUH.x.get() = 99;
+   |         ^^^^^^^^^^^^^^^^^
+
+thread 'rustc' panicked at 'assertion failed: `(left != right)`
+  left: `Const`,
+ right: `Const`: UnsafeCells are not allowed behind references in constants. This should have been prevented statically by const qualification. If this were allowed one would be able to change a constant at one use site and other use sites may arbitrarily decide to change, too.', src/librustc_mir/interpret/intern.rs:127:17
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
+
+error: internal compiler error: unexpected panic
+
+note: the compiler unexpectedly panicked. this is a bug.
+
+note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
+
+note: rustc VERSION running on TARGET
+
+note: compiler flags: FLAGS
+
diff --git a/src/test/ui/consts/packed_pattern.rs b/src/test/ui/consts/packed_pattern.rs
new file mode 100644
index 00000000000..37ae45b6df7
--- /dev/null
+++ b/src/test/ui/consts/packed_pattern.rs
@@ -0,0 +1,19 @@
+// run-pass
+
+#[derive(PartialEq, Eq, Copy, Clone)]
+#[repr(packed)]
+struct Foo {
+    field: (i64, u32, u32, u32),
+}
+
+const FOO: Foo = Foo {
+    field: (5, 6, 7, 8),
+};
+
+fn main() {
+    match FOO {
+        Foo { field: (5, 6, 7, 8) } => {},
+        FOO => unreachable!(),
+        _ => unreachable!(),
+    }
+}
diff --git a/src/test/ui/consts/packed_pattern2.rs b/src/test/ui/consts/packed_pattern2.rs
new file mode 100644
index 00000000000..174161fbd94
--- /dev/null
+++ b/src/test/ui/consts/packed_pattern2.rs
@@ -0,0 +1,27 @@
+// run-pass
+
+#[derive(PartialEq, Eq, Copy, Clone)]
+#[repr(packed)]
+struct Foo {
+    field: (u8, u16),
+}
+
+#[derive(PartialEq, Eq, Copy, Clone)]
+#[repr(align(2))]
+struct Bar {
+    a: Foo,
+}
+
+const FOO: Bar = Bar {
+    a: Foo {
+        field: (5, 6),
+    }
+};
+
+fn main() {
+    match FOO {
+        Bar { a: Foo { field: (5, 6) } } => {},
+        FOO => unreachable!(),
+        _ => unreachable!(),
+    }
+}
diff --git a/src/test/ui/consts/static-raw-pointer-interning.rs b/src/test/ui/consts/static-raw-pointer-interning.rs
new file mode 100644
index 00000000000..cab60c91e16
--- /dev/null
+++ b/src/test/ui/consts/static-raw-pointer-interning.rs
@@ -0,0 +1,15 @@
+// run-pass
+
+static FOO: Foo = Foo {
+    field: &42 as *const i32,
+};
+
+struct Foo {
+    field: *const i32,
+}
+
+unsafe impl Sync for Foo {}
+
+fn main() {
+    assert_eq!(unsafe { *FOO.field }, 42);
+}
diff --git a/src/test/ui/consts/static-raw-pointer-interning2.rs b/src/test/ui/consts/static-raw-pointer-interning2.rs
new file mode 100644
index 00000000000..2b915fd7cb3
--- /dev/null
+++ b/src/test/ui/consts/static-raw-pointer-interning2.rs
@@ -0,0 +1,15 @@
+// run-pass
+
+static mut FOO: Foo = Foo {
+    field: &mut [42] as *mut [i32] as *mut i32,
+};
+
+struct Foo {
+    field: *mut i32,
+}
+
+unsafe impl Sync for Foo {}
+
+fn main() {
+    assert_eq!(unsafe { *FOO.field = 69; *FOO.field }, 69);
+}
diff --git a/src/test/ui/consts/union_constant.rs b/src/test/ui/consts/union_constant.rs
index 6b6042194ba..16b05310b9f 100644
--- a/src/test/ui/consts/union_constant.rs
+++ b/src/test/ui/consts/union_constant.rs
@@ -6,5 +6,6 @@ union Uninit {
 }
 
 const UNINIT: Uninit = Uninit { uninit: () };
+const UNINIT2: (Uninit,) = (Uninit { uninit: () }, );
 
 fn main() {}