about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-12-02 08:35:24 +0000
committerbors <bors@rust-lang.org>2019-12-02 08:35:24 +0000
commit4af3ee8ee2a2bc1286b021db7600ba990359cf3f (patch)
tree8631bfc4d4f6746bdc573f206f8f5edf4bbdfc74 /src
parentf5c81e0a986e4285d3d0fd781a1bd475753eb12c (diff)
parent910e83eab2f65dd922f8d980c71840a68b3d7c6a (diff)
downloadrust-4af3ee8ee2a2bc1286b021db7600ba990359cf3f.tar.gz
rust-4af3ee8ee2a2bc1286b021db7600ba990359cf3f.zip
Auto merge of #66950 - RalfJung:rollup-12d0zx8, r=RalfJung
Rollup of 5 pull requests

Successful merges:

 - #66245 (Conditional compilation for sanitizers)
 - #66654 (Handle const-checks for `&mut` outside of `HasMutInterior`)
 - #66822 (libunwind_panic: adjust miri panic hack)
 - #66827 (handle diverging functions forwarding their return place)
 - #66834 (rustbuild fixes)

Failed merges:

r? @ghost
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/bootstrap.py12
-rw-r--r--src/bootstrap/install.rs2
-rw-r--r--src/doc/unstable-book/src/language-features/cfg-sanitize.md36
-rw-r--r--src/libcore/intrinsics.rs4
-rw-r--r--src/libpanic_unwind/lib.rs16
-rw-r--r--src/libpanic_unwind/miri.rs42
-rw-r--r--src/librustc/session/config.rs15
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs15
-rw-r--r--src/librustc_feature/active.rs3
-rw-r--r--src/librustc_feature/builtin_attrs.rs1
-rw-r--r--src/librustc_mir/interpret/place.rs38
-rw-r--r--src/librustc_mir/lib.rs1
-rw-r--r--src/librustc_mir/transform/check_consts/qualifs.rs37
-rw-r--r--src/librustc_mir/transform/check_consts/validation.rs192
-rw-r--r--src/libsyntax_pos/symbol.rs2
-rw-r--r--src/test/ui/consts/const-multi-ref.rs19
-rw-r--r--src/test/ui/consts/const-multi-ref.stderr13
-rw-r--r--src/test/ui/error-codes/E0017.rs3
-rw-r--r--src/test/ui/error-codes/E0017.stderr24
-rw-r--r--src/test/ui/error-codes/E0388.rs9
-rw-r--r--src/test/ui/error-codes/E0388.stderr28
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs3
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr12
-rw-r--r--src/test/ui/sanitize-cfg.rs26
24 files changed, 292 insertions, 261 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 730e8cf05d4..bb169414886 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -643,7 +643,9 @@ class RustBuild(object):
         env["LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib") + \
             (os.pathsep + env["LIBRARY_PATH"]) \
             if "LIBRARY_PATH" in env else ""
-        env["RUSTFLAGS"] = "-Cdebuginfo=2 "
+        # preserve existing RUSTFLAGS
+        env.setdefault("RUSTFLAGS", "")
+        env["RUSTFLAGS"] += " -Cdebuginfo=2"
 
         build_section = "target.{}".format(self.build_triple())
         target_features = []
@@ -652,13 +654,13 @@ class RustBuild(object):
         elif self.get_toml("crt-static", build_section) == "false":
             target_features += ["-crt-static"]
         if target_features:
-            env["RUSTFLAGS"] += "-C target-feature=" + (",".join(target_features)) + " "
+            env["RUSTFLAGS"] += " -C target-feature=" + (",".join(target_features))
         target_linker = self.get_toml("linker", build_section)
         if target_linker is not None:
-            env["RUSTFLAGS"] += "-C linker=" + target_linker + " "
-        env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes "
+            env["RUSTFLAGS"] += " -C linker=" + target_linker
+        env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
         if self.get_toml("deny-warnings", "rust") != "false":
-            env["RUSTFLAGS"] += "-Dwarnings "
+            env["RUSTFLAGS"] += " -Dwarnings"
 
         env["PATH"] = os.path.join(self.bin_root(), "bin") + \
             os.pathsep + env["PATH"]
diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs
index 384219c38fd..f8734ebdf42 100644
--- a/src/bootstrap/install.rs
+++ b/src/bootstrap/install.rs
@@ -260,7 +260,7 @@ install!((self, builder, _config),
     };
     Rustc, "src/librustc", true, only_hosts: true, {
         builder.ensure(dist::Rustc {
-            compiler: self.compiler,
+            compiler: builder.compiler(builder.top_stage, self.target),
         });
         install_rustc(builder, self.compiler.stage, self.target);
     };
diff --git a/src/doc/unstable-book/src/language-features/cfg-sanitize.md b/src/doc/unstable-book/src/language-features/cfg-sanitize.md
new file mode 100644
index 00000000000..949f24ab9c1
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/cfg-sanitize.md
@@ -0,0 +1,36 @@
+# `cfg_sanitize`
+
+The tracking issue for this feature is: [#39699]
+
+[#39699]: https://github.com/rust-lang/rust/issues/39699
+
+------------------------
+
+The `cfg_sanitize` feature makes it possible to execute different code
+depending on whether a particular sanitizer is enabled or not.
+
+## Examples
+
+``` rust
+#![feature(cfg_sanitize)]
+
+#[cfg(sanitize = "thread")]
+fn a() {
+  // ...
+}
+
+#[cfg(not(sanitize = "thread"))]
+fn a() {
+  // ...
+}
+
+fn b() {
+  if cfg!(sanitize = "leak") {
+    // ...
+  } else {
+    // ...
+  }
+}
+
+```
+
diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs
index e3dc5630c94..d4952f53bf7 100644
--- a/src/libcore/intrinsics.rs
+++ b/src/libcore/intrinsics.rs
@@ -1348,9 +1348,11 @@ extern "rust-intrinsic" {
     pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
 
     /// Internal hook used by Miri to implement unwinding.
+    /// Compiles to a NOP during non-Miri codegen.
+    ///
     /// Perma-unstable: do not use
     #[cfg(not(bootstrap))]
-    pub fn miri_start_panic(data: *mut (dyn crate::any::Any + crate::marker::Send)) -> !;
+    pub fn miri_start_panic(data: *mut (dyn crate::any::Any + crate::marker::Send)) -> ();
 }
 
 // Some functions are defined here because they accidentally got made
diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs
index 0c834e5c2a0..3a14197c77b 100644
--- a/src/libpanic_unwind/lib.rs
+++ b/src/libpanic_unwind/lib.rs
@@ -36,10 +36,7 @@ use core::raw;
 use core::panic::BoxMeUp;
 
 cfg_if::cfg_if! {
-    if #[cfg(miri)] {
-        #[path = "miri.rs"]
-        mod imp;
-    } else if #[cfg(target_os = "emscripten")] {
+    if #[cfg(target_os = "emscripten")] {
         #[path = "emcc.rs"]
         mod imp;
     } else if #[cfg(target_arch = "wasm32")] {
@@ -94,5 +91,14 @@ pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
 #[unwind(allowed)]
 pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
     let payload = payload as *mut &mut dyn BoxMeUp;
-    imp::panic(Box::from_raw((*payload).take_box()))
+    let payload = (*payload).take_box();
+
+    // Miri panic support: cfg'd out of normal builds just to be sure.
+    // When going through normal codegen, `miri_start_panic` is a NOP, so the
+    // Miri-enabled sysroot still supports normal unwinding. But when executed in
+    // Miri, this line initiates unwinding.
+    #[cfg(miri)]
+    core::intrinsics::miri_start_panic(payload);
+
+    imp::panic(Box::from_raw(payload))
 }
diff --git a/src/libpanic_unwind/miri.rs b/src/libpanic_unwind/miri.rs
deleted file mode 100644
index f26c42fd4bc..00000000000
--- a/src/libpanic_unwind/miri.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-#![allow(nonstandard_style)]
-
-use core::any::Any;
-use alloc::boxed::Box;
-
-pub fn payload() -> *mut u8 {
-    core::ptr::null_mut()
-}
-
-pub unsafe fn panic(data: Box<dyn Any + Send>) -> ! {
-    core::intrinsics::miri_start_panic(Box::into_raw(data))
-}
-
-pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
-    Box::from_raw(ptr)
-}
-
-// This is required by the compiler to exist (e.g., it's a lang item),
-// but is never used by Miri. Therefore, we just use a stub here
-#[lang = "eh_personality"]
-#[cfg(not(test))]
-fn rust_eh_personality() {
-    unsafe { core::intrinsics::abort() }
-}
-
-// The rest is required on *some* targets to exist (specifically, MSVC targets that use SEH).
-// We just add it on all targets. Copied from `seh.rs`.
-#[repr(C)]
-pub struct _TypeDescriptor {
-    pub pVFTable: *const u8,
-    pub spare: *mut u8,
-    pub name: [u8; 11],
-}
-
-const TYPE_NAME: [u8; 11] = *b"rust_panic\0";
-
-#[cfg_attr(not(test), lang = "eh_catch_typeinfo")]
-static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
-    pVFTable: core::ptr::null(),
-    spare: core::ptr::null_mut(),
-    name: TYPE_NAME,
-};
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index d2ac5436cc8..9a242d9d076 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -47,6 +47,17 @@ pub enum Sanitizer {
     Thread,
 }
 
+impl fmt::Display for Sanitizer {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            Sanitizer::Address => "address".fmt(f),
+            Sanitizer::Leak => "leak".fmt(f),
+            Sanitizer::Memory => "memory".fmt(f),
+            Sanitizer::Thread => "thread".fmt(f),
+        }
+    }
+}
+
 impl FromStr for Sanitizer {
     type Err = ();
     fn from_str(s: &str) -> Result<Sanitizer, ()> {
@@ -1580,6 +1591,10 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
             }
         }
     }
+    if let Some(s) = &sess.opts.debugging_opts.sanitizer {
+        let symbol = Symbol::intern(&s.to_string());
+        ret.insert((sym::sanitize, Some(symbol)));
+    }
     if sess.opts.debug_assertions {
         ret.insert((Symbol::intern("debug_assertions"), None));
     }
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index 14be0e80fb4..d76392f7570 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -528,18 +528,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             _ => FnAbi::new(&bx, sig, &extra_args)
         };
 
-        // This should never be reachable at runtime:
-        // We should only emit a call to this intrinsic in #[cfg(miri)] mode,
-        // which means that we will never actually use the generate object files
-        // (we will just be interpreting the MIR)
-        //
-        // Note that we still need to be able to codegen *something* for this intrisnic:
-        // Miri currently uses Xargo to build a special libstd. As a side effect,
-        // we generate normal object files for libstd - while these are never used,
-        // we still need to be able to build them.
+        // For normal codegen, this Miri-specific intrinsic is just a NOP.
         if intrinsic == Some("miri_start_panic") {
-            bx.abort();
-            bx.unreachable();
+            let target = destination.as_ref().unwrap().1;
+            helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
+            helper.funclet_br(self, &mut bx, target);
             return;
         }
 
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index 7c0d39965fc..16d8ada9f24 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -524,6 +524,9 @@ declare_features! (
     /// Allows the use of `if` and `match` in constants.
     (active, const_if_match, "1.41.0", Some(49146), None),
 
+    /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used.
+    (active, cfg_sanitize, "1.41.0", Some(39699), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs
index 9c936492cbd..4fa0198d871 100644
--- a/src/librustc_feature/builtin_attrs.rs
+++ b/src/librustc_feature/builtin_attrs.rs
@@ -25,6 +25,7 @@ const GATED_CFGS: &[GatedCfg] = &[
     (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
     (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
     (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
+    (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
 ];
 
 /// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 5b263f76801..70d9836b6ff 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -651,20 +651,28 @@ where
         use rustc::mir::PlaceBase;
 
         let mut place_ty = match &place.base {
-            PlaceBase::Local(mir::RETURN_PLACE) => match self.frame().return_place {
-                Some(return_place) => {
-                    // We use our layout to verify our assumption; caller will validate
-                    // their layout on return.
-                    PlaceTy {
-                        place: *return_place,
-                        layout: self.layout_of(
-                            self.subst_from_frame_and_normalize_erasing_regions(
-                                self.frame().body.return_ty()
-                            )
-                        )?,
-                    }
+            PlaceBase::Local(mir::RETURN_PLACE) => {
+                // `return_place` has the *caller* layout, but we want to use our
+                // `layout to verify our assumption. The caller will validate
+                // their layout on return.
+                PlaceTy {
+                    place: match self.frame().return_place {
+                        Some(p) => *p,
+                        // Even if we don't have a return place, we sometimes need to
+                        // create this place, but any attempt to read from / write to it
+                        // (even a ZST read/write) needs to error, so let us make this
+                        // a NULL place.
+                        //
+                        // FIXME: Ideally we'd make sure that the place projections also
+                        // bail out.
+                        None => Place::null(&*self),
+                    },
+                    layout: self.layout_of(
+                        self.subst_from_frame_and_normalize_erasing_regions(
+                            self.frame().body.return_ty()
+                        )
+                    )?,
                 }
-                None => throw_unsup!(InvalidNullPointerUsage),
             },
             PlaceBase::Local(local) => PlaceTy {
                 // This works even for dead/uninitialized locals; we check further when writing
@@ -791,8 +799,8 @@ where
         // to handle padding properly, which is only correct if we never look at this data with the
         // wrong type.
 
-        let ptr = match self.check_mplace_access(dest, None)
-            .expect("places should be checked on creation")
+        // Invalid places are a thing: the return place of a diverging function
+        let ptr = match self.check_mplace_access(dest, None)?
         {
             Some(ptr) => ptr,
             None => return Ok(()), // zero-sized access
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index cca76700ed1..f4fb9a5e4f2 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -29,6 +29,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 #![feature(stmt_expr_attributes)]
 #![feature(bool_to_option)]
 #![feature(trait_alias)]
+#![feature(matches_macro)]
 
 #![recursion_limit="256"]
 
diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs
index 9ed1ca740b8..2d5a0a2afcd 100644
--- a/src/librustc_mir/transform/check_consts/qualifs.rs
+++ b/src/librustc_mir/transform/check_consts/qualifs.rs
@@ -5,7 +5,7 @@ use rustc::ty::{self, Ty};
 use rustc::hir::def_id::DefId;
 use syntax_pos::DUMMY_SP;
 
-use super::{ConstKind, Item as ConstCx};
+use super::Item as ConstCx;
 
 pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
     ConstQualifs {
@@ -33,9 +33,10 @@ pub trait Qualif {
     /// of the type.
     fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
 
-    fn in_static(_cx: &ConstCx<'_, 'tcx>, _def_id: DefId) -> bool {
-        // FIXME(eddyb) should we do anything here for value properties?
-        false
+    fn in_static(cx: &ConstCx<'_, 'tcx>, def_id: DefId) -> bool {
+        // `mir_const_qualif` does return the qualifs in the final value of a `static`, so we could
+        // use value-based qualification here, but we shouldn't do this without a good reason.
+        Self::in_any_value_of_ty(cx, cx.tcx.type_of(def_id))
     }
 
     fn in_projection_structurally(
@@ -217,34 +218,6 @@ impl Qualif for HasMutInterior {
         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.
-                    match ty.kind {
-                        // Inside a `static mut`, &mut [...] is also allowed.
-                        | ty::Array(..)
-                        | ty::Slice(_)
-                        if cx.const_kind == Some(ConstKind::StaticMut)
-                        => {},
-
-                        // FIXME(eddyb): We only return false for `&mut []` outside a const
-                        // context which seems unnecessary given that this is merely a ZST.
-                        | ty::Array(_, len)
-                        if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
-                            && cx.const_kind == None
-                        => {},
-
-                        _ => return true,
-                    }
-                }
-            }
-
             Rvalue::Aggregate(ref kind, _) => {
                 if let AggregateKind::Adt(def, ..) = **kind {
                     if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs
index 829d9ee6faf..783c64ece73 100644
--- a/src/librustc_mir/transform/check_consts/validation.rs
+++ b/src/librustc_mir/transform/check_consts/validation.rs
@@ -23,13 +23,6 @@ use super::qualifs::{self, HasMutInterior, NeedsDrop};
 use super::resolver::FlowSensitiveAnalysis;
 use super::{ConstKind, Item, Qualif, is_lang_panic_fn};
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum CheckOpResult {
-    Forbidden,
-    Unleashed,
-    Allowed,
-}
-
 pub type IndirectlyMutableResults<'mir, 'tcx> =
     old_dataflow::DataflowResultsCursor<'mir, 'tcx, IndirectlyMutableLocals<'mir, 'tcx>>;
 
@@ -149,17 +142,6 @@ pub struct Validator<'a, 'mir, 'tcx> {
 
     /// The span of the current statement.
     span: Span,
-
-    /// True if the local was assigned the result of an illegal borrow (`ops::MutBorrow`).
-    ///
-    /// This is used to hide errors from {re,}borrowing the newly-assigned local, instead pointing
-    /// the user to the place where the illegal borrow occurred. This set is only populated once an
-    /// error has been emitted, so it will never cause an erroneous `mir::Body` to pass validation.
-    ///
-    /// FIXME(ecstaticmorse): assert at the end of checking that if `tcx.has_errors() == false`,
-    /// 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>,
 }
 
 impl Deref for Validator<'_, 'mir, 'tcx> {
@@ -213,7 +195,6 @@ impl Validator<'a, 'mir, 'tcx> {
             span: item.body.span,
             item,
             qualifs,
-            derived_from_illegal_borrow: BitSet::new_empty(item.body.local_decls.len()),
         }
     }
 
@@ -258,15 +239,15 @@ impl Validator<'a, 'mir, 'tcx> {
     }
 
     /// 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
+    /// context.
+    pub fn check_op_spanned<O>(&mut self, op: O, span: Span)
     where
         O: NonConstOp
     {
         trace!("check_op: op={:?}", op);
 
         if op.is_allowed_in_item(self) {
-            return CheckOpResult::Allowed;
+            return;
         }
 
         // If an operation is supported in miri (and is not already controlled by a feature gate) it
@@ -276,20 +257,19 @@ impl Validator<'a, 'mir, 'tcx> {
 
         if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
             self.tcx.sess.span_warn(span, "skipping const checks");
-            return CheckOpResult::Unleashed;
+            return;
         }
 
         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) -> CheckOpResult {
+    pub fn check_op(&mut self, op: impl NonConstOp) {
         let span = self.span;
         self.check_op_spanned(op, span)
     }
 
-    fn check_static(&mut self, def_id: DefId, span: Span) -> CheckOpResult {
+    fn check_static(&mut self, def_id: DefId, span: Span) {
         let is_thread_local = self.tcx.has_attr(def_id, sym::thread_local);
         if is_thread_local {
             self.check_op_spanned(ops::ThreadLocalAccess, span)
@@ -322,20 +302,9 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
 
-        // Check nested operands and places.
+        // Special-case reborrows to be more like a copy of a reference.
         if let Rvalue::Ref(_, kind, ref place) = *rvalue {
-            // Special-case reborrows to be more like a copy of a reference.
-            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 {
+            if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) {
                 let ctx = match kind {
                     BorrowKind::Shared => PlaceContext::NonMutatingUse(
                         NonMutatingUseContext::SharedBorrow,
@@ -351,14 +320,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
                     ),
                 };
                 self.visit_place_base(&place.base, ctx, location);
-                self.visit_projection(&place.base, proj, ctx, location);
-            } else {
-                self.super_rvalue(rvalue, location);
+                self.visit_projection(&place.base, reborrowed_proj, ctx, location);
+                return;
             }
-        } else {
-            self.super_rvalue(rvalue, location);
         }
 
+        self.super_rvalue(rvalue, location);
+
         match *rvalue {
             Rvalue::Use(_) |
             Rvalue::Repeat(..) |
@@ -369,9 +337,58 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
             Rvalue::Cast(CastKind::Pointer(_), ..) |
             Rvalue::Discriminant(..) |
             Rvalue::Len(_) |
-            Rvalue::Ref(..) |
             Rvalue::Aggregate(..) => {}
 
+            | Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
+            | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place)
+            => {
+                let ty = place.ty(self.body, self.tcx).ty;
+                let is_allowed = match ty.kind {
+                    // Inside a `static mut`, `&mut [...]` is allowed.
+                    ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut
+                        => true,
+
+                    // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
+                    // that this is merely a ZST and it is already eligible for promotion.
+                    // This may require an RFC?
+                    /*
+                    ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
+                        => true,
+                    */
+
+                    _ => false,
+                };
+
+                if !is_allowed {
+                    self.check_op(ops::MutBorrow(kind));
+                }
+            }
+
+            // At the moment, `PlaceBase::Static` is only used for promoted MIR.
+            | Rvalue::Ref(_, BorrowKind::Shared, ref place)
+            | Rvalue::Ref(_, BorrowKind::Shallow, ref place)
+            if matches!(place.base, PlaceBase::Static(_))
+            => bug!("Saw a promoted during const-checking, which must run before promotion"),
+
+            | Rvalue::Ref(_, kind @ BorrowKind::Shared, ref place)
+            | Rvalue::Ref(_, kind @ BorrowKind::Shallow, ref place)
+            => {
+                // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually
+                // seek the cursors beforehand.
+                self.qualifs.has_mut_interior.cursor.seek_before(location);
+                self.qualifs.indirectly_mutable.seek(location);
+
+                let borrowed_place_has_mut_interior = HasMutInterior::in_place(
+                    &self.item,
+                    &|local| self.qualifs.has_mut_interior_eager_seek(local),
+                    place.as_ref(),
+                );
+
+                if borrowed_place_has_mut_interior {
+                    self.check_op(ops::MutBorrow(kind));
+                }
+            }
+
             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");
@@ -436,58 +453,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
         }
     }
 
-    fn visit_assign(&mut self, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
-        trace!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
-
-        // Error on mutable borrows or shared borrows of values with interior mutability.
-        //
-        // This replicates the logic at the start of `assign` in the old const checker.  Note that
-        // it depends on `HasMutInterior` being set for mutable borrows as well as values with
-        // interior mutability.
-        if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
-            // FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually seek
-            // the cursors beforehand.
-            self.qualifs.has_mut_interior.cursor.seek_before(location);
-            self.qualifs.indirectly_mutable.seek(location);
-
-            let rvalue_has_mut_interior = HasMutInterior::in_rvalue(
-                &self.item,
-                &|local| self.qualifs.has_mut_interior_eager_seek(local),
-                rvalue,
-            );
-
-            if rvalue_has_mut_interior {
-                let is_derived_from_illegal_borrow = match borrowed_place.as_local() {
-                    // If an unprojected local was borrowed and its value was the result of an
-                    // illegal borrow, suppress this error and mark the result of this borrow as
-                    // illegal as well.
-                    Some(borrowed_local)
-                        if self.derived_from_illegal_borrow.contains(borrowed_local) =>
-                    {
-                        true
-                    }
-
-                    // Otherwise proceed normally: check the legality of a mutable borrow in this
-                    // context.
-                    _ => self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden,
-                };
-
-                // When the target of the assignment is a local with no projections, mark it as
-                // derived from an illegal borrow if necessary.
-                //
-                // FIXME: should we also clear `derived_from_illegal_borrow` when a local is
-                // assigned a new value?
-                if is_derived_from_illegal_borrow {
-                    if let Some(dest) = dest.as_local() {
-                        self.derived_from_illegal_borrow.insert(dest);
-                    }
-                }
-            }
-        }
-
-        self.super_assign(dest, rvalue, location);
-    }
-
     fn visit_projection_elem(
         &mut self,
         place_base: &PlaceBase<'tcx>,
@@ -724,3 +689,36 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId)
         }
     });
 }
+
+fn place_as_reborrow(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    place: &'a Place<'tcx>,
+) -> Option<&'a [PlaceElem<'tcx>]> {
+    place
+        .projection
+        .split_last()
+        .and_then(|(outermost, inner)| {
+            if outermost != &ProjectionElem::Deref {
+                return None;
+            }
+
+            // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
+            // that points to the allocation for the static. Don't treat these as reborrows.
+            if let PlaceBase::Local(local) = place.base {
+                if body.local_decls[local].is_ref_to_static() {
+                    return None;
+                }
+            }
+
+            // Ensure the type being derefed is a reference and not a raw pointer.
+            //
+            // This is sufficient to prevent an access to a `static mut` from being marked as a
+            // reborrow, even if the check above were to disappear.
+            let inner_ty = Place::ty_from(&place.base, inner, body, tcx).ty;
+            match inner_ty.kind {
+                ty::Ref(..) => Some(inner),
+                _ => None,
+            }
+        })
+}
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 7d43c3c8d07..3059b059691 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -177,6 +177,7 @@ symbols! {
         cfg_attr,
         cfg_attr_multi,
         cfg_doctest,
+        cfg_sanitize,
         cfg_target_feature,
         cfg_target_has_atomic,
         cfg_target_thread_local,
@@ -634,6 +635,7 @@ symbols! {
         rust_eh_unwind_resume,
         rust_oom,
         rvalue_static_promotion,
+        sanitize,
         sanitizer_runtime,
         _Self,
         self_in_typedefs,
diff --git a/src/test/ui/consts/const-multi-ref.rs b/src/test/ui/consts/const-multi-ref.rs
index 498e99e668b..5e2be0d4f3f 100644
--- a/src/test/ui/consts/const-multi-ref.rs
+++ b/src/test/ui/consts/const-multi-ref.rs
@@ -1,11 +1,24 @@
-const _X: i32 = {
+// Ensure that we point the user to the erroneous borrow but not to any subsequent borrows of that
+// initial one.
+
+const _: i32 = {
     let mut a = 5;
-    let p = &mut a;      //~ ERROR references in constants may only refer to immutable values
+    let p = &mut a; //~ ERROR references in constants may only refer to immutable values
 
-    let reborrow = {p};  //~ ERROR references in constants may only refer to immutable values
+    let reborrow = {p};
     let pp = &reborrow;
     let ppp = &pp;
     ***ppp
 };
 
+const _: std::cell::Cell<i32> = {
+    let mut a = std::cell::Cell::new(5);
+    let p = &a; //~ ERROR cannot borrow a constant which may contain interior mutability
+
+    let reborrow = {p};
+    let pp = &reborrow;
+    let ppp = &pp;
+    a
+};
+
 fn main() {}
diff --git a/src/test/ui/consts/const-multi-ref.stderr b/src/test/ui/consts/const-multi-ref.stderr
index 9e525ef9aac..ed3837e9c9e 100644
--- a/src/test/ui/consts/const-multi-ref.stderr
+++ b/src/test/ui/consts/const-multi-ref.stderr
@@ -1,15 +1,16 @@
 error[E0017]: references in constants may only refer to immutable values
-  --> $DIR/const-multi-ref.rs:3:13
+  --> $DIR/const-multi-ref.rs:6:13
    |
 LL |     let p = &mut a;
    |             ^^^^^^ constants require immutable values
 
-error[E0017]: references in constants may only refer to immutable values
-  --> $DIR/const-multi-ref.rs:5:21
+error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
+  --> $DIR/const-multi-ref.rs:16:13
    |
-LL |     let reborrow = {p};
-   |                     ^ constants require immutable values
+LL |     let p = &a;
+   |             ^^
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0017`.
+Some errors have detailed explanations: E0017, E0492.
+For more information about an error, try `rustc --explain E0017`.
diff --git a/src/test/ui/error-codes/E0017.rs b/src/test/ui/error-codes/E0017.rs
index 94b6587eb81..3bc518c2c2b 100644
--- a/src/test/ui/error-codes/E0017.rs
+++ b/src/test/ui/error-codes/E0017.rs
@@ -1,8 +1,11 @@
 static X: i32 = 1;
 const C: i32 = 2;
+static mut M: i32 = 3;
 
 const CR: &'static mut i32 = &mut C; //~ ERROR E0017
 static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
+                                              //~| ERROR E0019
                                               //~| ERROR cannot borrow
 static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
+static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0017
 fn main() {}
diff --git a/src/test/ui/error-codes/E0017.stderr b/src/test/ui/error-codes/E0017.stderr
index 47863f02214..8c8660adceb 100644
--- a/src/test/ui/error-codes/E0017.stderr
+++ b/src/test/ui/error-codes/E0017.stderr
@@ -1,28 +1,40 @@
 error[E0017]: references in constants may only refer to immutable values
-  --> $DIR/E0017.rs:4:30
+  --> $DIR/E0017.rs:5:30
    |
 LL | const CR: &'static mut i32 = &mut C;
    |                              ^^^^^^ constants require immutable values
 
+error[E0019]: static contains unimplemented expression type
+  --> $DIR/E0017.rs:6:39
+   |
+LL | static STATIC_REF: &'static mut i32 = &mut X;
+   |                                       ^^^^^^
+
 error[E0017]: references in statics may only refer to immutable values
-  --> $DIR/E0017.rs:5:39
+  --> $DIR/E0017.rs:6:39
    |
 LL | static STATIC_REF: &'static mut i32 = &mut X;
    |                                       ^^^^^^ statics require immutable values
 
 error[E0596]: cannot borrow immutable static item `X` as mutable
-  --> $DIR/E0017.rs:5:39
+  --> $DIR/E0017.rs:6:39
    |
 LL | static STATIC_REF: &'static mut i32 = &mut X;
    |                                       ^^^^^^ cannot borrow as mutable
 
 error[E0017]: references in statics may only refer to immutable values
-  --> $DIR/E0017.rs:7:38
+  --> $DIR/E0017.rs:9:38
    |
 LL | static CONST_REF: &'static mut i32 = &mut C;
    |                                      ^^^^^^ statics require immutable values
 
-error: aborting due to 4 previous errors
+error[E0017]: references in statics may only refer to immutable values
+  --> $DIR/E0017.rs:10:52
+   |
+LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M };
+   |                                                    ^^^^^^ statics require immutable values
+
+error: aborting due to 6 previous errors
 
-Some errors have detailed explanations: E0017, E0596.
+Some errors have detailed explanations: E0017, E0019, E0596.
 For more information about an error, try `rustc --explain E0017`.
diff --git a/src/test/ui/error-codes/E0388.rs b/src/test/ui/error-codes/E0388.rs
deleted file mode 100644
index 3aa4ac9655c..00000000000
--- a/src/test/ui/error-codes/E0388.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-static X: i32 = 1;
-const C: i32 = 2;
-
-const CR: &'static mut i32 = &mut C; //~ ERROR E0017
-static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
-                                              //~| ERROR cannot borrow
-static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0017
-
-fn main() {}
diff --git a/src/test/ui/error-codes/E0388.stderr b/src/test/ui/error-codes/E0388.stderr
deleted file mode 100644
index b52d5260b13..00000000000
--- a/src/test/ui/error-codes/E0388.stderr
+++ /dev/null
@@ -1,28 +0,0 @@
-error[E0017]: references in constants may only refer to immutable values
-  --> $DIR/E0388.rs:4:30
-   |
-LL | const CR: &'static mut i32 = &mut C;
-   |                              ^^^^^^ constants require immutable values
-
-error[E0017]: references in statics may only refer to immutable values
-  --> $DIR/E0388.rs:5:39
-   |
-LL | static STATIC_REF: &'static mut i32 = &mut X;
-   |                                       ^^^^^^ statics require immutable values
-
-error[E0596]: cannot borrow immutable static item `X` as mutable
-  --> $DIR/E0388.rs:5:39
-   |
-LL | static STATIC_REF: &'static mut i32 = &mut X;
-   |                                       ^^^^^^ cannot borrow as mutable
-
-error[E0017]: references in statics may only refer to immutable values
-  --> $DIR/E0388.rs:7:38
-   |
-LL | static CONST_REF: &'static mut i32 = &mut C;
-   |                                      ^^^^^^ statics require immutable values
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0017, E0596.
-For more information about an error, try `rustc --explain E0017`.
diff --git a/src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs
new file mode 100644
index 00000000000..c3e7cc9ed8a
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.rs
@@ -0,0 +1,3 @@
+#[cfg(not(sanitize = "thread"))]
+//~^ `cfg(sanitize)` is experimental
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr
new file mode 100644
index 00000000000..f67a0d83bdd
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg_sanitize.stderr
@@ -0,0 +1,12 @@
+error[E0658]: `cfg(sanitize)` is experimental and subject to change
+  --> $DIR/feature-gate-cfg_sanitize.rs:1:11
+   |
+LL | #[cfg(not(sanitize = "thread"))]
+   |           ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/39699
+   = help: add `#![feature(cfg_sanitize)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/sanitize-cfg.rs b/src/test/ui/sanitize-cfg.rs
new file mode 100644
index 00000000000..9c198543a86
--- /dev/null
+++ b/src/test/ui/sanitize-cfg.rs
@@ -0,0 +1,26 @@
+// Verifies that when compiling with -Zsanitizer=option,
+// the `#[cfg(sanitize = "option")]` attribute is configured.
+
+// needs-sanitizer-support
+// only-linux
+// only-x86_64
+// check-pass
+// revisions: address leak memory thread
+//[address]compile-flags: -Zsanitizer=address --cfg address
+//[leak]compile-flags:    -Zsanitizer=leak    --cfg leak
+//[memory]compile-flags:  -Zsanitizer=memory  --cfg memory
+//[thread]compile-flags:  -Zsanitizer=thread  --cfg thread
+
+#![feature(cfg_sanitize)]
+
+#[cfg(all(sanitize = "address", address))]
+fn main() {}
+
+#[cfg(all(sanitize = "leak", leak))]
+fn main() {}
+
+#[cfg(all(sanitize = "memory", memory))]
+fn main() {}
+
+#[cfg(all(sanitize = "thread", thread))]
+fn main() {}