about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-02-10 10:47:39 +0000
committerbors <bors@rust-lang.org>2020-02-10 10:47:39 +0000
commit4d1241f5158ffd66730e094d8f199ed654ed52ae (patch)
tree911cdc3247d263b7f8364c275ebc04a2eb867e64
parent840bdc349d2885a5173269b653025192969cfebc (diff)
parent18c6d39b5500909f38da7b76d1c51d54300c535e (diff)
downloadrust-4d1241f5158ffd66730e094d8f199ed654ed52ae.tar.gz
rust-4d1241f5158ffd66730e094d8f199ed654ed52ae.zip
Auto merge of #69012 - Dylan-DPC:rollup-13qn0fq, r=Dylan-DPC
Rollup of 6 pull requests

Successful merges:

 - #68694 (Reduce the number of `RefCell`s in `InferCtxt`.)
 - #68966 (Improve performance of coherence checks)
 - #68976 (Make `num::NonZeroX::new` an unstable `const fn`)
 - #68992 (Correctly parse `mut a @ b`)
 - #69005 (Small graphviz improvements for the new dataflow framework)
 - #69006 (parser: Keep current and previous tokens precisely)

Failed merges:

r? @ghost
-rw-r--r--src/libcore/num/mod.rs3
-rw-r--r--src/libgraphviz/lib.rs10
-rw-r--r--src/librustc/infer/canonical/canonicalizer.rs6
-rw-r--r--src/librustc/infer/combine.rs50
-rw-r--r--src/librustc/infer/equate.rs13
-rw-r--r--src/librustc/infer/error_reporting/need_type_info.rs13
-rw-r--r--src/librustc/infer/freshen.rs18
-rw-r--r--src/librustc/infer/fudge.rs32
-rw-r--r--src/librustc/infer/glb.rs7
-rw-r--r--src/librustc/infer/higher_ranked/mod.rs2
-rw-r--r--src/librustc/infer/lattice.rs4
-rw-r--r--src/librustc/infer/lub.rs7
-rw-r--r--src/librustc/infer/mod.rs336
-rw-r--r--src/librustc/infer/nll_relate/mod.rs10
-rw-r--r--src/librustc/infer/outlives/obligations.rs4
-rw-r--r--src/librustc/infer/resolve.rs11
-rw-r--r--src/librustc/infer/sub.rs13
-rw-r--r--src/librustc/infer/unify_key.rs3
-rw-r--r--src/librustc/query/mod.rs3
-rw-r--r--src/librustc/traits/auto_trait.rs16
-rw-r--r--src/librustc/traits/coherence.rs23
-rw-r--r--src/librustc/traits/mod.rs7
-rw-r--r--src/librustc/traits/project.rs12
-rw-r--r--src/librustc/traits/select.rs46
-rw-r--r--src/librustc/traits/specialize/mod.rs7
-rw-r--r--src/librustc/traits/specialize/specialization_graph.rs30
-rw-r--r--src/librustc_mir/dataflow/generic/engine.rs2
-rw-r--r--src/librustc_mir/dataflow/generic/graphviz.rs79
-rw-r--r--src/librustc_parse/parser/mod.rs74
-rw-r--r--src/librustc_parse/parser/pat.rs11
-rw-r--r--src/librustc_parse/parser/path.rs2
-rw-r--r--src/librustc_traits/chalk_context/resolvent_ops.rs4
-rw-r--r--src/librustc_typeck/coherence/builtin.rs12
-rw-r--r--src/librustc_typeck/coherence/inherent_impls_overlap.rs3
-rw-r--r--src/librustc_typeck/coherence/mod.rs84
-rw-r--r--src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr4
-rw-r--r--src/test/ui/consts/const-nonzero.rs11
-rw-r--r--src/test/ui/parser/mbe_missing_right_paren.stderr4
-rw-r--r--src/test/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs13
-rw-r--r--src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs13
-rw-r--r--src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr21
-rw-r--r--src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs13
-rw-r--r--src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr15
43 files changed, 606 insertions, 445 deletions
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index ed37b48b3e8..3ae8f0f7870 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -69,8 +69,9 @@ assert_eq!(size_of::<Option<core::num::", stringify!($Ty), ">>(), size_of::<", s
 
                 /// Creates a non-zero if the given value is not zero.
                 #[$stability]
+                #[rustc_const_unstable(feature = "const_nonzero_int_methods", issue = "53718")]
                 #[inline]
-                pub fn new(n: $Int) -> Option<Self> {
+                pub const fn new(n: $Int) -> Option<Self> {
                     if n != 0 {
                         // SAFETY: we just checked that there's no `0`
                         Some(unsafe { Self(n) })
diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs
index d04f5c1d4ee..a53e0012ca2 100644
--- a/src/libgraphviz/lib.rs
+++ b/src/libgraphviz/lib.rs
@@ -597,6 +597,8 @@ pub enum RenderOption {
     NoNodeLabels,
     NoEdgeStyles,
     NoNodeStyles,
+
+    Monospace,
 }
 
 /// Returns vec holding all the default render options.
@@ -626,6 +628,14 @@ where
     W: Write,
 {
     writeln!(w, "digraph {} {{", g.graph_id().as_slice())?;
+
+    // Global graph properties
+    if options.contains(&RenderOption::Monospace) {
+        writeln!(w, r#"    graph[fontname="monospace"];"#)?;
+        writeln!(w, r#"    node[fontname="monospace"];"#)?;
+        writeln!(w, r#"    edge[fontname="monospace"];"#)?;
+    }
+
     for n in g.nodes().iter() {
         write!(w, "    ")?;
         let id = g.node_id(n);
diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs
index 48a6c6d7413..85fafa34915 100644
--- a/src/librustc/infer/canonical/canonicalizer.rs
+++ b/src/librustc/infer/canonical/canonicalizer.rs
@@ -317,7 +317,9 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
                 let r = self
                     .infcx
                     .unwrap()
-                    .borrow_region_constraints()
+                    .inner
+                    .borrow_mut()
+                    .unwrap_region_constraints()
                     .opportunistic_resolve_var(self.tcx, vid);
                 debug!(
                     "canonical: region var found with vid {:?}, \
@@ -621,7 +623,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
 
     /// Returns the universe in which `vid` is defined.
     fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
-        self.infcx.unwrap().borrow_region_constraints().var_universe(vid)
+        self.infcx.unwrap().inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
     }
 
     /// Creates a canonical variable (with the given `info`)
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 2518805a1ec..9eb961255c2 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -74,8 +74,9 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
         match (&a.kind, &b.kind) {
             // Relate integral variables to other types
             (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => {
-                self.int_unification_table
+                self.inner
                     .borrow_mut()
+                    .int_unification_table
                     .unify_var_var(a_id, b_id)
                     .map_err(|e| int_unification_error(a_is_expected, e))?;
                 Ok(a)
@@ -95,8 +96,9 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
 
             // Relate floating-point variables to other types
             (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => {
-                self.float_unification_table
+                self.inner
                     .borrow_mut()
+                    .float_unification_table
                     .unify_var_var(a_id, b_id)
                     .map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
                 Ok(a)
@@ -131,8 +133,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
             return Ok(a);
         }
 
-        let a = replace_if_possible(self.const_unification_table.borrow_mut(), a);
-        let b = replace_if_possible(self.const_unification_table.borrow_mut(), b);
+        let a = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table, a);
+        let b = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table, b);
 
         let a_is_expected = relation.a_is_expected();
 
@@ -141,8 +143,9 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
                 ty::ConstKind::Infer(InferConst::Var(a_vid)),
                 ty::ConstKind::Infer(InferConst::Var(b_vid)),
             ) => {
-                self.const_unification_table
+                self.inner
                     .borrow_mut()
+                    .const_unification_table
                     .unify_var_var(a_vid, b_vid)
                     .map_err(|e| const_unification_error(a_is_expected, e))?;
                 return Ok(a);
@@ -174,8 +177,9 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
         vid: ty::ConstVid<'tcx>,
         value: &'tcx ty::Const<'tcx>,
     ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
-        self.const_unification_table
+        self.inner
             .borrow_mut()
+            .const_unification_table
             .unify_var_value(
                 vid,
                 ConstVarValue {
@@ -196,8 +200,9 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
         vid: ty::IntVid,
         val: ty::IntVarValue,
     ) -> RelateResult<'tcx, Ty<'tcx>> {
-        self.int_unification_table
+        self.inner
             .borrow_mut()
+            .int_unification_table
             .unify_var_value(vid, Some(val))
             .map_err(|e| int_unification_error(vid_is_expected, e))?;
         match val {
@@ -212,8 +217,9 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
         vid: ty::FloatVid,
         val: ast::FloatTy,
     ) -> RelateResult<'tcx, Ty<'tcx>> {
-        self.float_unification_table
+        self.inner
             .borrow_mut()
+            .float_unification_table
             .unify_var_value(vid, Some(ty::FloatVarValue(val)))
             .map_err(|e| float_unification_error(vid_is_expected, e))?;
         Ok(self.tcx.mk_mach_float(val))
@@ -260,7 +266,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         use self::RelationDir::*;
 
         // Get the actual variable that b_vid has been inferred to
-        debug_assert!(self.infcx.type_variables.borrow_mut().probe(b_vid).is_unknown());
+        debug_assert!(self.infcx.inner.borrow_mut().type_variables.probe(b_vid).is_unknown());
 
         debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid);
 
@@ -280,7 +286,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
             "instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})",
             a_ty, dir, b_vid, b_ty
         );
-        self.infcx.type_variables.borrow_mut().instantiate(b_vid, b_ty);
+        self.infcx.inner.borrow_mut().type_variables.instantiate(b_vid, b_ty);
 
         if needs_wf {
             self.obligations.push(Obligation::new(
@@ -338,7 +344,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
 
         debug!("generalize: ambient_variance = {:?}", ambient_variance);
 
-        let for_universe = match self.infcx.type_variables.borrow_mut().probe(for_vid) {
+        let for_universe = match self.infcx.inner.borrow_mut().type_variables.probe(for_vid) {
             v @ TypeVariableValue::Known { .. } => {
                 panic!("instantiating {:?} which has a known value {:?}", for_vid, v,)
             }
@@ -350,7 +356,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         let mut generalize = Generalizer {
             infcx: self.infcx,
             span: self.trace.cause.span,
-            for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
+            for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables.sub_root_var(for_vid),
             for_universe,
             ambient_variance,
             needs_wf: false,
@@ -502,17 +508,16 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
         // us from creating infinitely sized types.
         match t.kind {
             ty::Infer(ty::TyVar(vid)) => {
-                let mut variables = self.infcx.type_variables.borrow_mut();
-                let vid = variables.root_var(vid);
-                let sub_vid = variables.sub_root_var(vid);
+                let vid = self.infcx.inner.borrow_mut().type_variables.root_var(vid);
+                let sub_vid = self.infcx.inner.borrow_mut().type_variables.sub_root_var(vid);
                 if sub_vid == self.for_vid_sub_root {
                     // If sub-roots are equal, then `for_vid` and
                     // `vid` are related via subtyping.
                     Err(TypeError::CyclicTy(self.root_ty))
                 } else {
-                    match variables.probe(vid) {
+                    let probe = self.infcx.inner.borrow_mut().type_variables.probe(vid);
+                    match probe {
                         TypeVariableValue::Known { value: u } => {
-                            drop(variables);
                             debug!("generalize: known value {:?}", u);
                             self.relate(&u, &u)
                         }
@@ -536,8 +541,13 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                                 ty::Covariant | ty::Contravariant => (),
                             }
 
-                            let origin = *variables.var_origin(vid);
-                            let new_var_id = variables.new_var(self.for_universe, false, origin);
+                            let origin =
+                                *self.infcx.inner.borrow_mut().type_variables.var_origin(vid);
+                            let new_var_id = self.infcx.inner.borrow_mut().type_variables.new_var(
+                                self.for_universe,
+                                false,
+                                origin,
+                            );
                             let u = self.tcx().mk_ty_var(new_var_id);
                             debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
                             Ok(u)
@@ -612,7 +622,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
 
         match c.val {
             ty::ConstKind::Infer(InferConst::Var(vid)) => {
-                let mut variable_table = self.infcx.const_unification_table.borrow_mut();
+                let variable_table = &mut self.infcx.inner.borrow_mut().const_unification_table;
                 let var_value = variable_table.probe_value(vid);
                 match var_value.val {
                     ConstVariableValue::Known { value: u } => self.relate(&u, &u),
diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs
index f192295c1aa..018bbe03543 100644
--- a/src/librustc/infer/equate.rs
+++ b/src/librustc/infer/equate.rs
@@ -72,14 +72,14 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
         }
 
         let infcx = self.fields.infcx;
-        let a = infcx.type_variables.borrow_mut().replace_if_possible(a);
-        let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
+        let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a);
+        let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b);
 
         debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b);
 
         match (&a.kind, &b.kind) {
             (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
-                infcx.type_variables.borrow_mut().equate(a_id, b_id);
+                infcx.inner.borrow_mut().type_variables.equate(a_id, b_id);
             }
 
             (&ty::Infer(TyVar(a_id)), _) => {
@@ -105,7 +105,12 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
         debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
         let origin = Subtype(box self.fields.trace.clone());
-        self.fields.infcx.borrow_region_constraints().make_eqregion(origin, a, b);
+        self.fields
+            .infcx
+            .inner
+            .borrow_mut()
+            .unwrap_region_constraints()
+            .make_eqregion(origin, a, b);
         Ok(a)
     }
 
diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs
index 9947dea2340..0d7fce7eac6 100644
--- a/src/librustc/infer/error_reporting/need_type_info.rs
+++ b/src/librustc/infer/error_reporting/need_type_info.rs
@@ -47,9 +47,12 @@ impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
                 if ty.walk().any(|inner_ty| {
                     inner_ty == self.target_ty
                         || match (&inner_ty.kind, &self.target_ty.kind) {
-                            (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
-                                self.infcx.type_variables.borrow_mut().sub_unified(a_vid, b_vid)
-                            }
+                            (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => self
+                                .infcx
+                                .inner
+                                .borrow_mut()
+                                .type_variables
+                                .sub_unified(a_vid, b_vid),
                             _ => false,
                         }
                 }) {
@@ -166,7 +169,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         highlight: Option<ty::print::RegionHighlightMode>,
     ) -> (String, Option<Span>, Cow<'static, str>, Option<String>, Option<&'static str>) {
         if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind {
-            let ty_vars = self.type_variables.borrow();
+            let ty_vars = &self.inner.borrow().type_variables;
             let var_origin = ty_vars.var_origin(ty_vid);
             if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind {
                 let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id));
@@ -224,7 +227,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         let ty_to_string = |ty: Ty<'tcx>| -> String {
             let mut s = String::new();
             let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
-            let ty_vars = self.type_variables.borrow();
+            let ty_vars = &self.inner.borrow().type_variables;
             let getter = move |ty_vid| {
                 let var_origin = ty_vars.var_origin(ty_vid);
                 if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind {
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index cf61cac0ac4..0190989267b 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -154,14 +154,15 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
 
         match t.kind {
             ty::Infer(ty::TyVar(v)) => {
-                let opt_ty = self.infcx.type_variables.borrow_mut().probe(v).known();
+                let opt_ty = self.infcx.inner.borrow_mut().type_variables.probe(v).known();
                 self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)
             }
 
             ty::Infer(ty::IntVar(v)) => self.freshen_ty(
                 self.infcx
-                    .int_unification_table
+                    .inner
                     .borrow_mut()
+                    .int_unification_table
                     .probe_value(v)
                     .map(|v| v.to_type(tcx)),
                 ty::IntVar(v),
@@ -170,8 +171,9 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
 
             ty::Infer(ty::FloatVar(v)) => self.freshen_ty(
                 self.infcx
-                    .float_unification_table
+                    .inner
                     .borrow_mut()
+                    .float_unification_table
                     .probe_value(v)
                     .map(|v| v.to_type(tcx)),
                 ty::FloatVar(v),
@@ -225,8 +227,14 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
     fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
         match ct.val {
             ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
-                let opt_ct =
-                    self.infcx.const_unification_table.borrow_mut().probe_value(v).val.known();
+                let opt_ct = self
+                    .infcx
+                    .inner
+                    .borrow_mut()
+                    .const_unification_table
+                    .probe_value(v)
+                    .val
+                    .known();
                 return self.freshen_const(
                     opt_ct,
                     ty::InferConst::Var(v),
diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs
index 265e45635cf..d0b7bb32b98 100644
--- a/src/librustc/infer/fudge.rs
+++ b/src/librustc/infer/fudge.rs
@@ -8,11 +8,10 @@ use super::{ConstVariableOrigin, RegionVariableOrigin};
 use rustc_data_structures::unify as ut;
 use ut::UnifyKey;
 
-use std::cell::RefMut;
 use std::ops::Range;
 
 fn const_vars_since_snapshot<'tcx>(
-    mut table: RefMut<'_, ut::UnificationTable<ut::InPlace<ConstVid<'tcx>>>>,
+    table: &mut ut::UnificationTable<ut::InPlace<ConstVid<'tcx>>>,
     snapshot: &ut::Snapshot<ut::InPlace<ConstVid<'tcx>>>,
 ) -> (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>) {
     let range = table.vars_since_snapshot(snapshot);
@@ -82,23 +81,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     // going to be popped, so we will have to
                     // eliminate any references to them.
 
-                    let type_vars = self
-                        .type_variables
-                        .borrow_mut()
-                        .vars_since_snapshot(&snapshot.type_snapshot);
-                    let int_vars = self
-                        .int_unification_table
-                        .borrow_mut()
-                        .vars_since_snapshot(&snapshot.int_snapshot);
-                    let float_vars = self
-                        .float_unification_table
-                        .borrow_mut()
-                        .vars_since_snapshot(&snapshot.float_snapshot);
-                    let region_vars = self
-                        .borrow_region_constraints()
+                    let mut inner = self.inner.borrow_mut();
+                    let type_vars =
+                        inner.type_variables.vars_since_snapshot(&snapshot.type_snapshot);
+                    let int_vars =
+                        inner.int_unification_table.vars_since_snapshot(&snapshot.int_snapshot);
+                    let float_vars =
+                        inner.float_unification_table.vars_since_snapshot(&snapshot.float_snapshot);
+                    let region_vars = inner
+                        .unwrap_region_constraints()
                         .vars_since_snapshot(&snapshot.region_constraints_snapshot);
                     let const_vars = const_vars_since_snapshot(
-                        self.const_unification_table.borrow_mut(),
+                        &mut inner.const_unification_table,
                         &snapshot.const_snapshot,
                     );
 
@@ -166,7 +160,9 @@ impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> {
                     // variables to their binding anyhow, we know
                     // that it is unbound, so we can just return
                     // it.
-                    debug_assert!(self.infcx.type_variables.borrow_mut().probe(vid).is_unknown());
+                    debug_assert!(
+                        self.infcx.inner.borrow_mut().type_variables.probe(vid).is_unknown()
+                    );
                     ty
                 }
             }
diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs
index 293dc80d983..6ef92132bc7 100644
--- a/src/librustc/infer/glb.rs
+++ b/src/librustc/infer/glb.rs
@@ -66,7 +66,12 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
         debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
 
         let origin = Subtype(box self.fields.trace.clone());
-        Ok(self.fields.infcx.borrow_region_constraints().glb_regions(self.tcx(), origin, a, b))
+        Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions(
+            self.tcx(),
+            origin,
+            a,
+            b,
+        ))
     }
 
     fn consts(
diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs
index d25d186f4d7..1b0f399ca33 100644
--- a/src/librustc/infer/higher_ranked/mod.rs
+++ b/src/librustc/infer/higher_ranked/mod.rs
@@ -138,7 +138,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             return Ok(());
         }
 
-        self.borrow_region_constraints().leak_check(
+        self.inner.borrow_mut().unwrap_region_constraints().leak_check(
             self.tcx,
             overly_polymorphic,
             placeholder_map,
diff --git a/src/librustc/infer/lattice.rs b/src/librustc/infer/lattice.rs
index 3e0aa4727ae..df475af1151 100644
--- a/src/librustc/infer/lattice.rs
+++ b/src/librustc/infer/lattice.rs
@@ -56,8 +56,8 @@ where
     }
 
     let infcx = this.infcx();
-    let a = infcx.type_variables.borrow_mut().replace_if_possible(a);
-    let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
+    let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a);
+    let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b);
     match (&a.kind, &b.kind) {
         // If one side is known to be a variable and one is not,
         // create a variable (`v`) to represent the LUB. Make sure to
diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs
index b512d3df3e8..6a699f803c7 100644
--- a/src/librustc/infer/lub.rs
+++ b/src/librustc/infer/lub.rs
@@ -66,7 +66,12 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
         debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
 
         let origin = Subtype(box self.fields.trace.clone());
-        Ok(self.fields.infcx.borrow_region_constraints().lub_regions(self.tcx(), origin, a, b))
+        Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions(
+            self.tcx(),
+            origin,
+            a,
+            b,
+        ))
     }
 
     fn consts(
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index b93f4408cdc..be58de996a5 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -29,7 +29,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
-use std::cell::{Cell, Ref, RefCell, RefMut};
+use std::cell::{Cell, Ref, RefCell};
 use std::collections::BTreeMap;
 use std::fmt;
 use syntax::ast;
@@ -105,6 +105,89 @@ impl SuppressRegionErrors {
     }
 }
 
+/// This type contains all the things within `InferCtxt` that sit within a
+/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot
+/// operations are hot enough that we want only one call to `borrow_mut` per
+/// call to `start_snapshot` and `rollback_to`.
+pub struct InferCtxtInner<'tcx> {
+    /// Cache for projections. This cache is snapshotted along with the infcx.
+    ///
+    /// Public so that `traits::project` can use it.
+    pub projection_cache: traits::ProjectionCache<'tcx>,
+
+    /// We instantiate `UnificationTable` with `bounds<Ty>` because the types
+    /// that might instantiate a general type variable have an order,
+    /// represented by its upper and lower bounds.
+    type_variables: type_variable::TypeVariableTable<'tcx>,
+
+    /// Map from const parameter variable to the kind of const it represents.
+    const_unification_table: ut::UnificationTable<ut::InPlace<ty::ConstVid<'tcx>>>,
+
+    /// Map from integral variable to the kind of integer it represents.
+    int_unification_table: ut::UnificationTable<ut::InPlace<ty::IntVid>>,
+
+    /// Map from floating variable to the kind of float it represents.
+    float_unification_table: ut::UnificationTable<ut::InPlace<ty::FloatVid>>,
+
+    /// Tracks the set of region variables and the constraints between them.
+    /// This is initially `Some(_)` but when
+    /// `resolve_regions_and_report_errors` is invoked, this gets set to `None`
+    /// -- further attempts to perform unification, etc., may fail if new
+    /// region constraints would've been added.
+    region_constraints: Option<RegionConstraintCollector<'tcx>>,
+
+    /// A set of constraints that regionck must validate. Each
+    /// constraint has the form `T:'a`, meaning "some type `T` must
+    /// outlive the lifetime 'a". These constraints derive from
+    /// instantiated type parameters. So if you had a struct defined
+    /// like
+    ///
+    ///     struct Foo<T:'static> { ... }
+    ///
+    /// then in some expression `let x = Foo { ... }` it will
+    /// instantiate the type parameter `T` with a fresh type `$0`. At
+    /// the same time, it will record a region obligation of
+    /// `$0:'static`. This will get checked later by regionck. (We
+    /// can't generally check these things right away because we have
+    /// to wait until types are resolved.)
+    ///
+    /// These are stored in a map keyed to the id of the innermost
+    /// enclosing fn body / static initializer expression. This is
+    /// because the location where the obligation was incurred can be
+    /// relevant with respect to which sublifetime assumptions are in
+    /// place. The reason that we store under the fn-id, and not
+    /// something more fine-grained, is so that it is easier for
+    /// regionck to be sure that it has found *all* the region
+    /// obligations (otherwise, it's easy to fail to walk to a
+    /// particular node-id).
+    ///
+    /// Before running `resolve_regions_and_report_errors`, the creator
+    /// of the inference context is expected to invoke
+    /// `process_region_obligations` (defined in `self::region_obligations`)
+    /// for each body-id in this map, which will process the
+    /// obligations within. This is expected to be done 'late enough'
+    /// that all type inference variables have been bound and so forth.
+    pub region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>,
+}
+
+impl<'tcx> InferCtxtInner<'tcx> {
+    fn new() -> InferCtxtInner<'tcx> {
+        InferCtxtInner {
+            projection_cache: Default::default(),
+            type_variables: type_variable::TypeVariableTable::new(),
+            const_unification_table: ut::UnificationTable::new(),
+            int_unification_table: ut::UnificationTable::new(),
+            float_unification_table: ut::UnificationTable::new(),
+            region_constraints: Some(RegionConstraintCollector::new()),
+            region_obligations: vec![],
+        }
+    }
+
+    pub fn unwrap_region_constraints(&mut self) -> &mut RegionConstraintCollector<'tcx> {
+        self.region_constraints.as_mut().expect("region constraints already solved")
+    }
+}
+
 pub struct InferCtxt<'a, 'tcx> {
     pub tcx: TyCtxt<'tcx>,
 
@@ -114,16 +197,7 @@ pub struct InferCtxt<'a, 'tcx> {
     /// and for error reporting logic to read arbitrary node types.
     pub in_progress_tables: Option<&'a RefCell<ty::TypeckTables<'tcx>>>,
 
-    /// Cache for projections. This cache is snapshotted along with the
-    /// infcx.
-    ///
-    /// Public so that `traits::project` can use it.
-    pub projection_cache: RefCell<traits::ProjectionCache<'tcx>>,
-
-    /// We instantiate `UnificationTable` with `bounds<Ty>` because the
-    /// types that might instantiate a general type variable have an
-    /// order, represented by its upper and lower bounds.
-    pub type_variables: RefCell<type_variable::TypeVariableTable<'tcx>>,
+    pub inner: RefCell<InferCtxtInner<'tcx>>,
 
     /// If set, this flag causes us to skip the 'leak check' during
     /// higher-ranked subtyping operations. This flag is a temporary one used
@@ -132,22 +206,6 @@ pub struct InferCtxt<'a, 'tcx> {
     /// when entering a snapshot.
     skip_leak_check: Cell<bool>,
 
-    /// Map from const parameter variable to the kind of const it represents.
-    const_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::ConstVid<'tcx>>>>,
-
-    /// Map from integral variable to the kind of integer it represents.
-    int_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::IntVid>>>,
-
-    /// Map from floating variable to the kind of float it represents
-    float_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::FloatVid>>>,
-
-    /// Tracks the set of region variables and the constraints between
-    /// them.  This is initially `Some(_)` but when
-    /// `resolve_regions_and_report_errors` is invoked, this gets set
-    /// to `None` -- further attempts to perform unification etc may
-    /// fail if new region constraints would've been added.
-    region_constraints: RefCell<Option<RegionConstraintCollector<'tcx>>>,
-
     /// Once region inference is done, the values for each variable.
     lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>,
 
@@ -189,39 +247,6 @@ pub struct InferCtxt<'a, 'tcx> {
     /// This flag is true while there is an active snapshot.
     in_snapshot: Cell<bool>,
 
-    /// A set of constraints that regionck must validate. Each
-    /// constraint has the form `T:'a`, meaning "some type `T` must
-    /// outlive the lifetime 'a". These constraints derive from
-    /// instantiated type parameters. So if you had a struct defined
-    /// like
-    ///
-    ///     struct Foo<T:'static> { ... }
-    ///
-    /// then in some expression `let x = Foo { ... }` it will
-    /// instantiate the type parameter `T` with a fresh type `$0`. At
-    /// the same time, it will record a region obligation of
-    /// `$0:'static`. This will get checked later by regionck. (We
-    /// can't generally check these things right away because we have
-    /// to wait until types are resolved.)
-    ///
-    /// These are stored in a map keyed to the id of the innermost
-    /// enclosing fn body / static initializer expression. This is
-    /// because the location where the obligation was incurred can be
-    /// relevant with respect to which sublifetime assumptions are in
-    /// place. The reason that we store under the fn-id, and not
-    /// something more fine-grained, is so that it is easier for
-    /// regionck to be sure that it has found *all* the region
-    /// obligations (otherwise, it's easy to fail to walk to a
-    /// particular node-id).
-    ///
-    /// Before running `resolve_regions_and_report_errors`, the creator
-    /// of the inference context is expected to invoke
-    /// `process_region_obligations` (defined in `self::region_obligations`)
-    /// for each body-id in this map, which will process the
-    /// obligations within. This is expected to be done 'late enough'
-    /// that all type inference variables have been bound and so forth.
-    pub region_obligations: RefCell<Vec<(hir::HirId, RegionObligation<'tcx>)>>,
-
     /// What is the innermost universe we have created? Starts out as
     /// `UniverseIndex::root()` but grows from there as we enter
     /// universal quantifiers.
@@ -543,12 +568,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
             f(InferCtxt {
                 tcx,
                 in_progress_tables,
-                projection_cache: Default::default(),
-                type_variables: RefCell::new(type_variable::TypeVariableTable::new()),
-                const_unification_table: RefCell::new(ut::UnificationTable::new()),
-                int_unification_table: RefCell::new(ut::UnificationTable::new()),
-                float_unification_table: RefCell::new(ut::UnificationTable::new()),
-                region_constraints: RefCell::new(Some(RegionConstraintCollector::new())),
+                inner: RefCell::new(InferCtxtInner::new()),
                 lexical_region_resolutions: RefCell::new(None),
                 selection_cache: Default::default(),
                 evaluation_cache: Default::default(),
@@ -558,7 +578,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
                 err_count_on_creation: tcx.sess.err_count(),
                 in_snapshot: Cell::new(false),
                 skip_leak_check: Cell::new(false),
-                region_obligations: RefCell::new(vec![]),
                 universe: Cell::new(ty::UniverseIndex::ROOT),
             })
         })
@@ -616,7 +635,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool {
         match ty.kind {
-            ty::Infer(ty::TyVar(vid)) => self.type_variables.borrow().var_diverges(vid),
+            ty::Infer(ty::TyVar(vid)) => self.inner.borrow().type_variables.var_diverges(vid),
             _ => false,
         }
     }
@@ -630,14 +649,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         use crate::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt};
         match ty.kind {
             ty::Infer(ty::IntVar(vid)) => {
-                if self.int_unification_table.borrow_mut().probe_value(vid).is_some() {
+                if self.inner.borrow_mut().int_unification_table.probe_value(vid).is_some() {
                     Neither
                 } else {
                     UnconstrainedInt
                 }
             }
             ty::Infer(ty::FloatVar(vid)) => {
-                if self.float_unification_table.borrow_mut().probe_value(vid).is_some() {
+                if self.inner.borrow_mut().float_unification_table.probe_value(vid).is_some() {
                     Neither
                 } else {
                     UnconstrainedFloat
@@ -648,28 +667,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> {
-        let mut type_variables = self.type_variables.borrow_mut();
-        let mut int_unification_table = self.int_unification_table.borrow_mut();
-        let mut float_unification_table = self.float_unification_table.borrow_mut();
+        let mut inner = self.inner.borrow_mut();
         // FIXME(const_generics): should there be an equivalent function for const variables?
 
-        type_variables
+        let mut vars: Vec<Ty<'_>> = inner
+            .type_variables
             .unsolved_variables()
             .into_iter()
             .map(|t| self.tcx.mk_ty_var(t))
-            .chain(
-                (0..int_unification_table.len())
-                    .map(|i| ty::IntVid { index: i as u32 })
-                    .filter(|&vid| int_unification_table.probe_value(vid).is_none())
-                    .map(|v| self.tcx.mk_int_var(v)),
-            )
-            .chain(
-                (0..float_unification_table.len())
-                    .map(|i| ty::FloatVid { index: i as u32 })
-                    .filter(|&vid| float_unification_table.probe_value(vid).is_none())
-                    .map(|v| self.tcx.mk_float_var(v)),
-            )
-            .collect()
+            .collect();
+        vars.extend(
+            (0..inner.int_unification_table.len())
+                .map(|i| ty::IntVid { index: i as u32 })
+                .filter(|&vid| inner.int_unification_table.probe_value(vid).is_none())
+                .map(|v| self.tcx.mk_int_var(v)),
+        );
+        vars.extend(
+            (0..inner.float_unification_table.len())
+                .map(|i| ty::FloatVid { index: i as u32 })
+                .filter(|&vid| inner.float_unification_table.probe_value(vid).is_none())
+                .map(|v| self.tcx.mk_float_var(v)),
+        );
+        vars
     }
 
     fn combine_fields(
@@ -719,14 +738,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         let in_snapshot = self.in_snapshot.get();
         self.in_snapshot.set(true);
 
+        let mut inner = self.inner.borrow_mut();
         CombinedSnapshot {
-            projection_cache_snapshot: self.projection_cache.borrow_mut().snapshot(),
-            type_snapshot: self.type_variables.borrow_mut().snapshot(),
-            const_snapshot: self.const_unification_table.borrow_mut().snapshot(),
-            int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
-            float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
-            region_constraints_snapshot: self.borrow_region_constraints().start_snapshot(),
-            region_obligations_snapshot: self.region_obligations.borrow().len(),
+            projection_cache_snapshot: inner.projection_cache.snapshot(),
+            type_snapshot: inner.type_variables.snapshot(),
+            const_snapshot: inner.const_unification_table.snapshot(),
+            int_snapshot: inner.int_unification_table.snapshot(),
+            float_snapshot: inner.float_unification_table.snapshot(),
+            region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(),
+            region_obligations_snapshot: inner.region_obligations.len(),
             universe: self.universe(),
             was_in_snapshot: in_snapshot,
             was_skip_leak_check: self.skip_leak_check.get(),
@@ -756,13 +776,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         self.universe.set(universe);
         self.skip_leak_check.set(was_skip_leak_check);
 
-        self.projection_cache.borrow_mut().rollback_to(projection_cache_snapshot);
-        self.type_variables.borrow_mut().rollback_to(type_snapshot);
-        self.const_unification_table.borrow_mut().rollback_to(const_snapshot);
-        self.int_unification_table.borrow_mut().rollback_to(int_snapshot);
-        self.float_unification_table.borrow_mut().rollback_to(float_snapshot);
-        self.region_obligations.borrow_mut().truncate(region_obligations_snapshot);
-        self.borrow_region_constraints().rollback_to(region_constraints_snapshot);
+        let mut inner = self.inner.borrow_mut();
+        inner.projection_cache.rollback_to(projection_cache_snapshot);
+        inner.type_variables.rollback_to(type_snapshot);
+        inner.const_unification_table.rollback_to(const_snapshot);
+        inner.int_unification_table.rollback_to(int_snapshot);
+        inner.float_unification_table.rollback_to(float_snapshot);
+        inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot);
+        inner.region_obligations.truncate(region_obligations_snapshot);
     }
 
     fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) {
@@ -784,12 +805,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         self.in_snapshot.set(was_in_snapshot);
         self.skip_leak_check.set(was_skip_leak_check);
 
-        self.projection_cache.borrow_mut().commit(projection_cache_snapshot);
-        self.type_variables.borrow_mut().commit(type_snapshot);
-        self.const_unification_table.borrow_mut().commit(const_snapshot);
-        self.int_unification_table.borrow_mut().commit(int_snapshot);
-        self.float_unification_table.borrow_mut().commit(float_snapshot);
-        self.borrow_region_constraints().commit(region_constraints_snapshot);
+        let mut inner = self.inner.borrow_mut();
+        inner.projection_cache.commit(projection_cache_snapshot);
+        inner.type_variables.commit(type_snapshot);
+        inner.const_unification_table.commit(const_snapshot);
+        inner.int_unification_table.commit(int_snapshot);
+        inner.float_unification_table.commit(float_snapshot);
+        inner.unwrap_region_constraints().commit(region_constraints_snapshot);
     }
 
     /// Executes `f` and commit the bindings.
@@ -859,12 +881,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         snapshot: &CombinedSnapshot<'a, 'tcx>,
     ) -> Option<bool> {
-        self.borrow_region_constraints()
+        self.inner
+            .borrow_mut()
+            .unwrap_region_constraints()
             .region_constraints_added_in_snapshot(&snapshot.region_constraints_snapshot)
     }
 
     pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) {
-        self.borrow_region_constraints().add_given(sub, sup);
+        self.inner.borrow_mut().unwrap_region_constraints().add_given(sub, sup);
     }
 
     pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx>
@@ -900,7 +924,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         b: ty::Region<'tcx>,
     ) {
         debug!("sub_regions({:?} <: {:?})", a, b);
-        self.borrow_region_constraints().make_subregion(origin, a, b);
+        self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b);
     }
 
     /// Require that the region `r` be equal to one of the regions in
@@ -914,7 +938,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         in_regions: &Lrc<Vec<ty::Region<'tcx>>>,
     ) {
         debug!("member_constraint({:?} <: {:?})", region, in_regions);
-        self.borrow_region_constraints().member_constraint(
+        self.inner.borrow_mut().unwrap_region_constraints().member_constraint(
             opaque_type_def_id,
             definition_span,
             hidden_ty,
@@ -978,7 +1002,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
-        self.type_variables.borrow_mut().new_var(self.universe(), diverging, origin)
+        self.inner.borrow_mut().type_variables.new_var(self.universe(), diverging, origin)
     }
 
     pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
@@ -990,7 +1014,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         origin: TypeVariableOrigin,
         universe: ty::UniverseIndex,
     ) -> Ty<'tcx> {
-        let vid = self.type_variables.borrow_mut().new_var(universe, false, origin);
+        let vid = self.inner.borrow_mut().type_variables.new_var(universe, false, origin);
         self.tcx.mk_ty_var(vid)
     }
 
@@ -1013,21 +1037,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         universe: ty::UniverseIndex,
     ) -> &'tcx ty::Const<'tcx> {
         let vid = self
-            .const_unification_table
+            .inner
             .borrow_mut()
+            .const_unification_table
             .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } });
         self.tcx.mk_const_var(vid, ty)
     }
 
     pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> {
-        self.const_unification_table.borrow_mut().new_key(ConstVarValue {
+        self.inner.borrow_mut().const_unification_table.new_key(ConstVarValue {
             origin,
             val: ConstVariableValue::Unknown { universe: self.universe() },
         })
     }
 
     fn next_int_var_id(&self) -> IntVid {
-        self.int_unification_table.borrow_mut().new_key(None)
+        self.inner.borrow_mut().int_unification_table.new_key(None)
     }
 
     pub fn next_int_var(&self) -> Ty<'tcx> {
@@ -1035,7 +1060,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     fn next_float_var_id(&self) -> FloatVid {
-        self.float_unification_table.borrow_mut().new_key(None)
+        self.inner.borrow_mut().float_unification_table.new_key(None)
     }
 
     pub fn next_float_var(&self) -> Ty<'tcx> {
@@ -1057,7 +1082,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         origin: RegionVariableOrigin,
         universe: ty::UniverseIndex,
     ) -> ty::Region<'tcx> {
-        let region_var = self.borrow_region_constraints().new_region_var(universe, origin);
+        let region_var =
+            self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, origin);
         self.tcx.mk_region(ty::ReVar(region_var))
     }
 
@@ -1067,12 +1093,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// placeholders, however, it will return the universe which which
     /// they are associated.
     fn universe_of_region(&self, r: ty::Region<'tcx>) -> ty::UniverseIndex {
-        self.borrow_region_constraints().universe(r)
+        self.inner.borrow_mut().unwrap_region_constraints().universe(r)
     }
 
     /// Number of region variables created so far.
     pub fn num_region_vars(&self) -> usize {
-        self.borrow_region_constraints().num_region_vars()
+        self.inner.borrow_mut().unwrap_region_constraints().num_region_vars()
     }
 
     /// Just a convenient wrapper of `next_region_var` for using during NLL.
@@ -1105,7 +1131,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 // used in a path such as `Foo::<T, U>::new()` will
                 // use an inference variable for `C` with `[T, U]`
                 // as the substitutions for the default, `(T, U)`.
-                let ty_var_id = self.type_variables.borrow_mut().new_var(
+                let ty_var_id = self.inner.borrow_mut().type_variables.new_var(
                     self.universe(),
                     false,
                     TypeVariableOrigin {
@@ -1125,7 +1151,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     span,
                 };
                 let const_var_id =
-                    self.const_unification_table.borrow_mut().new_key(ConstVarValue {
+                    self.inner.borrow_mut().const_unification_table.new_key(ConstVarValue {
                         origin,
                         val: ConstVariableValue::Unknown { universe: self.universe() },
                     });
@@ -1179,9 +1205,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         suppress: SuppressRegionErrors,
     ) {
         assert!(
-            self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(),
+            self.is_tainted_by_errors() || self.inner.borrow().region_obligations.is_empty(),
             "region_obligations not empty: {:#?}",
-            self.region_obligations.borrow()
+            self.inner.borrow().region_obligations
         );
 
         let region_rels = &RegionRelations::new(
@@ -1191,8 +1217,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             outlives_env.free_region_map(),
         );
         let (var_infos, data) = self
-            .region_constraints
+            .inner
             .borrow_mut()
+            .region_constraints
             .take()
             .expect("regions already resolved")
             .into_infos_and_data();
@@ -1224,12 +1251,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// understands. See the NLL module for mode details.
     pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> {
         assert!(
-            self.region_obligations.borrow().is_empty(),
+            self.inner.borrow().region_obligations.is_empty(),
             "region_obligations not empty: {:#?}",
-            self.region_obligations.borrow()
+            self.inner.borrow().region_obligations
         );
 
-        self.borrow_region_constraints().take_and_reset_data()
+        self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data()
     }
 
     /// Gives temporary access to the region constraint data.
@@ -1238,8 +1265,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         op: impl FnOnce(&RegionConstraintData<'tcx>) -> R,
     ) -> R {
-        let region_constraints = self.borrow_region_constraints();
-        op(region_constraints.data())
+        let mut inner = self.inner.borrow_mut();
+        op(inner.unwrap_region_constraints().data())
     }
 
     /// Takes ownership of the list of variable regions. This implies
@@ -1249,8 +1276,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// of the set of region variables into the NLL region context.
     pub fn take_region_var_origins(&self) -> VarInfos {
         let (var_infos, data) = self
-            .region_constraints
+            .inner
             .borrow_mut()
+            .region_constraints
             .take()
             .expect("regions already resolved")
             .into_infos_and_data();
@@ -1276,7 +1304,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     pub fn probe_ty_var(&self, vid: TyVid) -> Result<Ty<'tcx>, ty::UniverseIndex> {
         use self::type_variable::TypeVariableValue;
 
-        match self.type_variables.borrow_mut().probe(vid) {
+        match self.inner.borrow_mut().type_variables.probe(vid) {
             TypeVariableValue::Known { value } => Ok(value),
             TypeVariableValue::Unknown { universe } => Err(universe),
         }
@@ -1299,7 +1327,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid {
-        self.type_variables.borrow_mut().root_var(var)
+        self.inner.borrow_mut().type_variables.root_var(var)
     }
 
     /// Where possible, replaces type/const variables in
@@ -1337,7 +1365,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         vid: ty::ConstVid<'tcx>,
     ) -> Result<&'tcx ty::Const<'tcx>, ty::UniverseIndex> {
-        match self.const_unification_table.borrow_mut().probe_value(vid).val {
+        match self.inner.borrow_mut().const_unification_table.probe_value(vid).val {
             ConstVariableValue::Known { value } => Ok(value),
             ConstVariableValue::Unknown { universe } => Err(universe),
         }
@@ -1434,7 +1462,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     ) {
         debug!("verify_generic_bound({:?}, {:?} <: {:?})", kind, a, bound);
 
-        self.borrow_region_constraints().verify_generic_bound(origin, kind, a, bound);
+        self.inner
+            .borrow_mut()
+            .unwrap_region_constraints()
+            .verify_generic_bound(origin, kind, a, bound);
     }
 
     pub fn type_is_copy_modulo_regions(
@@ -1509,19 +1540,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         InferOk { value, obligations }
     }
 
-    pub fn borrow_region_constraints(&self) -> RefMut<'_, RegionConstraintCollector<'tcx>> {
-        RefMut::map(self.region_constraints.borrow_mut(), |c| {
-            c.as_mut().expect("region constraints already solved")
-        })
-    }
-
     /// Clears the selection, evaluation, and projection caches. This is useful when
     /// repeatedly attempting to select an `Obligation` while changing only
     /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing.
     pub fn clear_caches(&self) {
         self.selection_cache.clear();
         self.evaluation_cache.clear();
-        self.projection_cache.borrow_mut().clear();
+        self.inner.borrow_mut().projection_cache.clear();
     }
 
     fn universe(&self) -> ty::UniverseIndex {
@@ -1562,27 +1587,27 @@ impl<'a, 'tcx> ShallowResolver<'a, 'tcx> {
                 // structurally), and we prevent cycles in any case,
                 // so this recursion should always be of very limited
                 // depth.
-                self.infcx
-                    .type_variables
-                    .borrow_mut()
-                    .probe(v)
-                    .known()
-                    .map(|t| self.fold_ty(t))
-                    .unwrap_or(typ)
+                //
+                // Note: if these two lines are combined into one we get
+                // dynamic borrow errors on `self.infcx.inner`.
+                let known = self.infcx.inner.borrow_mut().type_variables.probe(v).known();
+                known.map(|t| self.fold_ty(t)).unwrap_or(typ)
             }
 
             ty::Infer(ty::IntVar(v)) => self
                 .infcx
-                .int_unification_table
+                .inner
                 .borrow_mut()
+                .int_unification_table
                 .probe_value(v)
                 .map(|v| v.to_type(self.infcx.tcx))
                 .unwrap_or(typ),
 
             ty::Infer(ty::FloatVar(v)) => self
                 .infcx
-                .float_unification_table
+                .inner
                 .borrow_mut()
+                .float_unification_table
                 .probe_value(v)
                 .map(|v| v.to_type(self.infcx.tcx))
                 .unwrap_or(typ),
@@ -1603,7 +1628,7 @@ impl<'a, 'tcx> ShallowResolver<'a, 'tcx> {
 
                 // If `inlined_probe` returns a `Known` value its `kind` never
                 // matches `infer`.
-                match self.infcx.type_variables.borrow_mut().inlined_probe(v) {
+                match self.infcx.inner.borrow_mut().type_variables.inlined_probe(v) {
                     TypeVariableValue::Unknown { .. } => false,
                     TypeVariableValue::Known { .. } => true,
                 }
@@ -1613,7 +1638,7 @@ impl<'a, 'tcx> ShallowResolver<'a, 'tcx> {
                 // If inlined_probe_value returns a value it's always a
                 // `ty::Int(_)` or `ty::UInt(_)`, which nevers matches a
                 // `ty::Infer(_)`.
-                self.infcx.int_unification_table.borrow_mut().inlined_probe_value(v).is_some()
+                self.infcx.inner.borrow_mut().int_unification_table.inlined_probe_value(v).is_some()
             }
 
             ty::FloatVar(v) => {
@@ -1621,7 +1646,7 @@ impl<'a, 'tcx> ShallowResolver<'a, 'tcx> {
                 // `ty::Float(_)`, which nevers matches a `ty::Infer(_)`.
                 //
                 // Not `inlined_probe_value(v)` because this call site is colder.
-                self.infcx.float_unification_table.borrow_mut().probe_value(v).is_some()
+                self.infcx.inner.borrow_mut().float_unification_table.probe_value(v).is_some()
             }
 
             _ => unreachable!(),
@@ -1641,8 +1666,9 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> {
     fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
         if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = ct {
             self.infcx
-                .const_unification_table
+                .inner
                 .borrow_mut()
+                .const_unification_table
                 .probe_value(*vid)
                 .val
                 .known()
diff --git a/src/librustc/infer/nll_relate/mod.rs b/src/librustc/infer/nll_relate/mod.rs
index 6af004f7776..77e20e6ad8f 100644
--- a/src/librustc/infer/nll_relate/mod.rs
+++ b/src/librustc/infer/nll_relate/mod.rs
@@ -322,7 +322,7 @@ where
         match value_ty.kind {
             ty::Infer(ty::TyVar(value_vid)) => {
                 // Two type variables: just equate them.
-                self.infcx.type_variables.borrow_mut().equate(vid, value_vid);
+                self.infcx.inner.borrow_mut().type_variables.equate(vid, value_vid);
                 return Ok(value_ty);
             }
 
@@ -343,7 +343,7 @@ where
             assert!(!generalized_ty.has_infer_types());
         }
 
-        self.infcx.type_variables.borrow_mut().instantiate(vid, generalized_ty);
+        self.infcx.inner.borrow_mut().type_variables.instantiate(vid, generalized_ty);
 
         // The generalized values we extract from `canonical_var_values` have
         // been fully instantiated and hence the set of scopes we have
@@ -373,7 +373,7 @@ where
             delegate: &mut self.delegate,
             first_free_index: ty::INNERMOST,
             ambient_variance: self.ambient_variance,
-            for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
+            for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables.sub_root_var(for_vid),
             universe,
         };
 
@@ -870,7 +870,7 @@ where
             }
 
             ty::Infer(ty::TyVar(vid)) => {
-                let mut variables = self.infcx.type_variables.borrow_mut();
+                let variables = &mut self.infcx.inner.borrow_mut().type_variables;
                 let vid = variables.root_var(vid);
                 let sub_vid = variables.sub_root_var(vid);
                 if sub_vid == self.for_vid_sub_root {
@@ -972,7 +972,7 @@ where
                 bug!("unexpected inference variable encountered in NLL generalization: {:?}", a);
             }
             ty::ConstKind::Infer(InferConst::Var(vid)) => {
-                let mut variable_table = self.infcx.const_unification_table.borrow_mut();
+                let variable_table = &mut self.infcx.inner.borrow_mut().const_unification_table;
                 let var_value = variable_table.probe_value(vid);
                 match var_value.val.known() {
                     Some(u) => self.relate(&u, &u),
diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs
index 45e4a84589e..17153ef9724 100644
--- a/src/librustc/infer/outlives/obligations.rs
+++ b/src/librustc/infer/outlives/obligations.rs
@@ -82,7 +82,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
     ) {
         debug!("register_region_obligation(body_id={:?}, obligation={:?})", body_id, obligation);
 
-        self.region_obligations.borrow_mut().push((body_id, obligation));
+        self.inner.borrow_mut().region_obligations.push((body_id, obligation));
     }
 
     pub fn register_region_obligation_with_cause(
@@ -103,7 +103,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
 
     /// Trait queries just want to pass back type obligations "as is"
     pub fn take_registered_region_obligations(&self) -> Vec<(hir::HirId, RegionObligation<'tcx>)> {
-        ::std::mem::take(&mut *self.region_obligations.borrow_mut())
+        ::std::mem::take(&mut self.inner.borrow_mut().region_obligations)
     }
 
     /// Process the region obligations that must be proven (during
diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs
index d7dc6070442..c9acd1cf4a1 100644
--- a/src/librustc/infer/resolve.rs
+++ b/src/librustc/infer/resolve.rs
@@ -75,9 +75,12 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticTypeAndRegionResolver<'a, 'tcx>
 
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
-            ty::ReVar(rid) => {
-                self.infcx.borrow_region_constraints().opportunistic_resolve_var(self.tcx(), rid)
-            }
+            ty::ReVar(rid) => self
+                .infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_var(self.tcx(), rid),
             _ => r,
         }
     }
@@ -120,7 +123,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> {
                 // Since we called `shallow_resolve` above, this must
                 // be an (as yet...) unresolved inference variable.
                 let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty {
-                    let ty_vars = self.infcx.type_variables.borrow();
+                    let ty_vars = &self.infcx.inner.borrow().type_variables;
                     if let TypeVariableOrigin {
                         kind: TypeVariableOriginKind::TypeParameterDefinition(_, _),
                         span,
diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs
index f355ffe1962..ef4903358d5 100644
--- a/src/librustc/infer/sub.rs
+++ b/src/librustc/infer/sub.rs
@@ -80,8 +80,8 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
         }
 
         let infcx = self.fields.infcx;
-        let a = infcx.type_variables.borrow_mut().replace_if_possible(a);
-        let b = infcx.type_variables.borrow_mut().replace_if_possible(b);
+        let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a);
+        let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b);
         match (&a.kind, &b.kind) {
             (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => {
                 // Shouldn't have any LBR here, so we can safely put
@@ -95,7 +95,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
                 // have to record in the `type_variables` tracker that
                 // the two variables are equal modulo subtyping, which
                 // is important to the occurs check later on.
-                infcx.type_variables.borrow_mut().sub(a_vid, b_vid);
+                infcx.inner.borrow_mut().type_variables.sub(a_vid, b_vid);
                 self.fields.obligations.push(Obligation::new(
                     self.fields.trace.cause.clone(),
                     self.fields.param_env,
@@ -140,7 +140,12 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
         // from the "cause" field, we could perhaps give more tailored
         // error messages.
         let origin = SubregionOrigin::Subtype(box self.fields.trace.clone());
-        self.fields.infcx.borrow_region_constraints().make_subregion(origin, a, b);
+        self.fields
+            .infcx
+            .inner
+            .borrow_mut()
+            .unwrap_region_constraints()
+            .make_subregion(origin, a, b);
 
         Ok(a)
     }
diff --git a/src/librustc/infer/unify_key.rs b/src/librustc/infer/unify_key.rs
index d88188538fc..e205453a48c 100644
--- a/src/librustc/infer/unify_key.rs
+++ b/src/librustc/infer/unify_key.rs
@@ -4,7 +4,6 @@ use rustc_data_structures::unify::{EqUnifyValue, NoError, UnificationTable, Unif
 use rustc_span::symbol::Symbol;
 use rustc_span::{Span, DUMMY_SP};
 
-use std::cell::RefMut;
 use std::cmp;
 use std::marker::PhantomData;
 
@@ -214,7 +213,7 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> {
 impl<'tcx> EqUnifyValue for &'tcx ty::Const<'tcx> {}
 
 pub fn replace_if_possible(
-    mut table: RefMut<'_, UnificationTable<InPlace<ty::ConstVid<'tcx>>>>,
+    table: &mut UnificationTable<InPlace<ty::ConstVid<'tcx>>>,
     c: &'tcx ty::Const<'tcx>,
 ) -> &'tcx ty::Const<'tcx> {
     if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = c {
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index 7cb1f3aa0e8..425f14a5740 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -647,7 +647,8 @@ rustc_queries! {
         query trait_impls_of(key: DefId) -> &'tcx ty::trait_def::TraitImpls {
             desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) }
         }
-        query specialization_graph_of(_: DefId) -> &'tcx specialization_graph::Graph {
+        query specialization_graph_of(key: DefId) -> &'tcx specialization_graph::Graph {
+            desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) }
             cache_on_disk_if { true }
         }
         query is_object_safe(key: DefId) -> bool {
diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs
index d775393a808..1255728de37 100644
--- a/src/librustc/traits/auto_trait.rs
+++ b/src/librustc/traits/auto_trait.rs
@@ -199,12 +199,22 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                 panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, e)
             });
 
-            let body_id_map: FxHashMap<_, _> =
-                infcx.region_obligations.borrow().iter().map(|&(id, _)| (id, vec![])).collect();
+            let body_id_map: FxHashMap<_, _> = infcx
+                .inner
+                .borrow()
+                .region_obligations
+                .iter()
+                .map(|&(id, _)| (id, vec![]))
+                .collect();
 
             infcx.process_registered_region_obligations(&body_id_map, None, full_env);
 
-            let region_data = infcx.borrow_region_constraints().region_constraint_data().clone();
+            let region_data = infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .region_constraint_data()
+                .clone();
 
             let vid_to_region = self.map_vid_to_region(&region_data);
 
diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs
index 855da0367de..2a667b53550 100644
--- a/src/librustc/traits/coherence.rs
+++ b/src/librustc/traits/coherence.rs
@@ -6,7 +6,6 @@
 
 use crate::infer::{CombinedSnapshot, InferOk};
 use crate::traits::select::IntercrateAmbiguityCause;
-use crate::traits::IntercrateMode;
 use crate::traits::SkipLeakCheck;
 use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext};
 use crate::ty::fold::TypeFoldable;
@@ -27,7 +26,7 @@ enum InCrate {
 #[derive(Debug, Copy, Clone)]
 pub enum Conflict {
     Upstream,
-    Downstream { used_to_be_broken: bool },
+    Downstream,
 }
 
 pub struct OverlapResult<'tcx> {
@@ -53,7 +52,6 @@ pub fn overlapping_impls<F1, F2, R>(
     tcx: TyCtxt<'_>,
     impl1_def_id: DefId,
     impl2_def_id: DefId,
-    intercrate_mode: IntercrateMode,
     skip_leak_check: SkipLeakCheck,
     on_overlap: F1,
     no_overlap: F2,
@@ -65,13 +63,12 @@ where
     debug!(
         "overlapping_impls(\
            impl1_def_id={:?}, \
-           impl2_def_id={:?},
-           intercrate_mode={:?})",
-        impl1_def_id, impl2_def_id, intercrate_mode
+           impl2_def_id={:?})",
+        impl1_def_id, impl2_def_id,
     );
 
     let overlaps = tcx.infer_ctxt().enter(|infcx| {
-        let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
+        let selcx = &mut SelectionContext::intercrate(&infcx);
         overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some()
     });
 
@@ -83,7 +80,7 @@ where
     // this time tracking intercrate ambuiguity causes for better
     // diagnostics. (These take time and can lead to false errors.)
     tcx.infer_ctxt().enter(|infcx| {
-        let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
+        let selcx = &mut SelectionContext::intercrate(&infcx);
         selcx.enable_tracking_intercrate_ambiguity_causes();
         on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap())
     })
@@ -202,15 +199,7 @@ pub fn trait_ref_is_knowable<'tcx>(
     if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
         // A downstream or cousin crate is allowed to implement some
         // substitution of this trait-ref.
-
-        // A trait can be implementable for a trait ref by both the current
-        // crate and crates downstream of it. Older versions of rustc
-        // were not aware of this, causing incoherence (issue #43355).
-        let used_to_be_broken = orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok();
-        if used_to_be_broken {
-            debug!("trait_ref_is_knowable({:?}) - USED TO BE BROKEN", trait_ref);
-        }
-        return Some(Conflict::Downstream { used_to_be_broken });
+        return Some(Conflict::Downstream);
     }
 
     if trait_ref_is_local_or_fundamental(tcx, trait_ref) {
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 783807b5c3b..417b52c38a7 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -78,13 +78,6 @@ pub use self::chalk_fulfill::{
 
 pub use self::types::*;
 
-/// Whether to enable bug compatibility with issue #43355.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum IntercrateMode {
-    Issue43355,
-    Fixed,
-}
-
 /// Whether to skip the leak check, as part of a future compatibility warning step.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum SkipLeakCheck {
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index e0eb731f9b2..5d9f4ddfd16 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -486,7 +486,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
     // bounds. It might be the case that we want two distinct caches,
     // or else another kind of cache entry.
 
-    let cache_result = infcx.projection_cache.borrow_mut().try_start(cache_key);
+    let cache_result = infcx.inner.borrow_mut().projection_cache.try_start(cache_key);
     match cache_result {
         Ok(()) => {}
         Err(ProjectionCacheEntry::Ambiguous) => {
@@ -560,7 +560,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             // Once we have inferred everything we need to know, we
             // can ignore the `obligations` from that point on.
             if infcx.unresolved_type_vars(&ty.value).is_none() {
-                infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
+                infcx.inner.borrow_mut().projection_cache.complete_normalized(cache_key, &ty);
             // No need to extend `obligations`.
             } else {
                 obligations.extend(ty.obligations);
@@ -627,7 +627,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             };
 
             let cache_value = prune_cache_value_obligations(infcx, &result);
-            infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
+            infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, cache_value);
             obligations.extend(result.obligations);
             Some(result.value)
         }
@@ -638,7 +638,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
                 projected_ty
             );
             let result = Normalized { value: projected_ty, obligations: vec![] };
-            infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
+            infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, result.clone());
             // No need to extend `obligations`.
             Some(result.value)
         }
@@ -647,7 +647,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
                 "opt_normalize_projection_type: \
                  too many candidates"
             );
-            infcx.projection_cache.borrow_mut().ambiguous(cache_key);
+            infcx.inner.borrow_mut().projection_cache.ambiguous(cache_key);
             None
         }
         Err(ProjectionTyError::TraitSelectionError(_)) => {
@@ -657,7 +657,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             // Trait`, which when processed will cause the error to be
             // reported later
 
-            infcx.projection_cache.borrow_mut().error(cache_key);
+            infcx.inner.borrow_mut().projection_cache.error(cache_key);
             let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
             obligations.extend(result.obligations);
             Some(result.value)
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 26f2a4ddb38..cfeb392f87f 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -19,8 +19,8 @@ use super::DerivedObligationCause;
 use super::Selection;
 use super::SelectionResult;
 use super::TraitNotObjectSafe;
+use super::TraitQueryMode;
 use super::{BuiltinDerivedObligation, ImplDerivedObligation, ObligationCauseCode};
-use super::{IntercrateMode, TraitQueryMode};
 use super::{ObjectCastObligation, Obligation};
 use super::{ObligationCause, PredicateObligation, TraitObligation};
 use super::{OutputTypeParameterMismatch, Overflow, SelectionError, Unimplemented};
@@ -80,7 +80,7 @@ pub struct SelectionContext<'cx, 'tcx> {
     /// other words, we consider `$0: Bar` to be unimplemented if
     /// there is no type that the user could *actually name* that
     /// would satisfy it. This avoids crippling inference, basically.
-    intercrate: Option<IntercrateMode>,
+    intercrate: bool,
 
     intercrate_ambiguity_causes: Option<Vec<IntercrateAmbiguityCause>>,
 
@@ -218,22 +218,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx,
             freshener: infcx.freshener(),
-            intercrate: None,
+            intercrate: false,
             intercrate_ambiguity_causes: None,
             allow_negative_impls: false,
             query_mode: TraitQueryMode::Standard,
         }
     }
 
-    pub fn intercrate(
-        infcx: &'cx InferCtxt<'cx, 'tcx>,
-        mode: IntercrateMode,
-    ) -> SelectionContext<'cx, 'tcx> {
-        debug!("intercrate({:?})", mode);
+    pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx,
             freshener: infcx.freshener(),
-            intercrate: Some(mode),
+            intercrate: true,
             intercrate_ambiguity_causes: None,
             allow_negative_impls: false,
             query_mode: TraitQueryMode::Standard,
@@ -248,7 +244,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx,
             freshener: infcx.freshener(),
-            intercrate: None,
+            intercrate: false,
             intercrate_ambiguity_causes: None,
             allow_negative_impls,
             query_mode: TraitQueryMode::Standard,
@@ -263,7 +259,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         SelectionContext {
             infcx,
             freshener: infcx.freshener(),
-            intercrate: None,
+            intercrate: false,
             intercrate_ambiguity_causes: None,
             allow_negative_impls: false,
             query_mode,
@@ -276,7 +272,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// false overflow results (#47139) and because it costs
     /// computation time.
     pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) {
-        assert!(self.intercrate.is_some());
+        assert!(self.intercrate);
         assert!(self.intercrate_ambiguity_causes.is_none());
         self.intercrate_ambiguity_causes = Some(vec![]);
         debug!("selcx: enable_tracking_intercrate_ambiguity_causes");
@@ -286,7 +282,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     /// was enabled and disables tracking at the same time. If
     /// tracking is not enabled, just returns an empty vector.
     pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec<IntercrateAmbiguityCause> {
-        assert!(self.intercrate.is_some());
+        assert!(self.intercrate);
         self.intercrate_ambiguity_causes.take().unwrap_or(vec![])
     }
 
@@ -513,7 +509,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         if let Some(key) =
                             ProjectionCacheKey::from_poly_projection_predicate(self, data)
                         {
-                            self.infcx.projection_cache.borrow_mut().complete(key);
+                            self.infcx.inner.borrow_mut().projection_cache.complete(key);
                         }
                         result
                     }
@@ -562,7 +558,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) -> Result<EvaluationResult, OverflowError> {
         debug!("evaluate_trait_predicate_recursively({:?})", obligation);
 
-        if self.intercrate.is_none()
+        if !self.intercrate
             && obligation.is_global()
             && obligation.param_env.caller_bounds.iter().all(|bound| bound.needs_subst())
         {
@@ -727,7 +723,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             stack.fresh_trait_ref.skip_binder().input_types().any(|ty| ty.is_fresh());
         // This check was an imperfect workaround for a bug in the old
         // intercrate mode; it should be removed when that goes away.
-        if unbound_input_types && self.intercrate == Some(IntercrateMode::Issue43355) {
+        if unbound_input_types && self.intercrate {
             debug!(
                 "evaluate_stack({:?}) --> unbound argument, intercrate -->  ambiguous",
                 stack.fresh_trait_ref
@@ -1206,7 +1202,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> {
         debug!("is_knowable(intercrate={:?})", self.intercrate);
 
-        if !self.intercrate.is_some() {
+        if !self.intercrate {
             return None;
         }
 
@@ -1218,17 +1214,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // bound regions.
         let trait_ref = predicate.skip_binder().trait_ref;
 
-        let result = coherence::trait_ref_is_knowable(self.tcx(), trait_ref);
-        if let (
-            Some(Conflict::Downstream { used_to_be_broken: true }),
-            Some(IntercrateMode::Issue43355),
-        ) = (result, self.intercrate)
-        {
-            debug!("is_knowable: IGNORING conflict to be bug-compatible with #43355");
-            None
-        } else {
-            result
-        }
+        coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
     }
 
     /// Returns `true` if the global caches can be used.
@@ -1249,7 +1235,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // the master cache. Since coherence executes pretty quickly,
         // it's not worth going to more trouble to increase the
         // hit-rate, I don't think.
-        if self.intercrate.is_some() {
+        if self.intercrate {
             return false;
         }
 
@@ -3305,7 +3291,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return Err(());
         }
 
-        if self.intercrate.is_none()
+        if !self.intercrate
             && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation
         {
             debug!("match_impl: reservation impls only apply in intercrate mode");
diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs
index 8b68d6f2603..071b5277dd9 100644
--- a/src/librustc/traits/specialize/mod.rs
+++ b/src/librustc/traits/specialize/mod.rs
@@ -332,14 +332,9 @@ pub(super) fn specialization_graph_provider(
                 let impl_span =
                     tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
                 let mut err = match used_to_be_allowed {
-                    Some(FutureCompatOverlapErrorKind::Issue43355) | None => {
-                        struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg)
-                    }
+                    None => struct_span_err!(tcx.sess, impl_span, E0119, "{}", msg),
                     Some(kind) => {
                         let lint = match kind {
-                            FutureCompatOverlapErrorKind::Issue43355 => {
-                                unreachable!("converted to hard error above")
-                            }
                             FutureCompatOverlapErrorKind::Issue33140 => {
                                 ORDER_DEPENDENT_TRAIT_OBJECTS
                             }
diff --git a/src/librustc/traits/specialize/specialization_graph.rs b/src/librustc/traits/specialize/specialization_graph.rs
index 98908e672f0..ca7740199ec 100644
--- a/src/librustc/traits/specialize/specialization_graph.rs
+++ b/src/librustc/traits/specialize/specialization_graph.rs
@@ -9,7 +9,6 @@ pub use rustc::traits::types::specialization_graph::*;
 
 #[derive(Copy, Clone, Debug)]
 pub enum FutureCompatOverlapErrorKind {
-    Issue43355,
     Issue33140,
     LeakCheck,
 }
@@ -107,16 +106,16 @@ impl<'tcx> Children {
                 }
             };
 
+            let allowed_to_overlap =
+                tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling);
+
             let (le, ge) = traits::overlapping_impls(
                 tcx,
                 possible_sibling,
                 impl_def_id,
-                traits::IntercrateMode::Issue43355,
                 traits::SkipLeakCheck::default(),
                 |overlap| {
-                    if let Some(overlap_kind) =
-                        tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
-                    {
+                    if let Some(overlap_kind) = &allowed_to_overlap {
                         match overlap_kind {
                             ty::ImplOverlapKind::Permitted { marker: _ } => {}
                             ty::ImplOverlapKind::Issue33140 => {
@@ -154,31 +153,14 @@ impl<'tcx> Children {
 
                 replace_children.push(possible_sibling);
             } else {
-                if let None = tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
-                    // do future-compat checks for overlap. Have issue #33140
-                    // errors overwrite issue #43355 errors when both are present.
-
-                    traits::overlapping_impls(
-                        tcx,
-                        possible_sibling,
-                        impl_def_id,
-                        traits::IntercrateMode::Fixed,
-                        traits::SkipLeakCheck::default(),
-                        |overlap| {
-                            last_lint = Some(FutureCompatOverlapError {
-                                error: overlap_error(overlap),
-                                kind: FutureCompatOverlapErrorKind::Issue43355,
-                            });
-                        },
-                        || (),
-                    );
+                if let None = allowed_to_overlap {
+                    // Do future-compat checks for overlap.
 
                     if last_lint.is_none() {
                         traits::overlapping_impls(
                             tcx,
                             possible_sibling,
                             impl_def_id,
-                            traits::IntercrateMode::Fixed,
                             traits::SkipLeakCheck::Yes,
                             |overlap| {
                                 last_lint = Some(FutureCompatOverlapError {
diff --git a/src/librustc_mir/dataflow/generic/engine.rs b/src/librustc_mir/dataflow/generic/engine.rs
index c0152b0c7d5..718c1e9ae20 100644
--- a/src/librustc_mir/dataflow/generic/engine.rs
+++ b/src/librustc_mir/dataflow/generic/engine.rs
@@ -331,7 +331,7 @@ where
     let mut buf = Vec::new();
 
     let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter);
-    dot::render(&graphviz, &mut buf)?;
+    dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?;
     fs::write(&path, buf)?;
     Ok(())
 }
diff --git a/src/librustc_mir/dataflow/generic/graphviz.rs b/src/librustc_mir/dataflow/generic/graphviz.rs
index fdf86e7b53f..b805b13592f 100644
--- a/src/librustc_mir/dataflow/generic/graphviz.rs
+++ b/src/librustc_mir/dataflow/generic/graphviz.rs
@@ -171,10 +171,19 @@ where
         //   | | (on successful return)           | +_4        |
         //   +-+----------------------------------+------------+
 
-        write!(
-            w,
-            r#"<table border="1" cellborder="1" cellspacing="0" cellpadding="3" sides="rb">"#,
-        )?;
+        // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their
+        // children. This is because `xdot` seemed to have a hard time correctly propagating
+        // attributes. Make sure to test the output before trying to remove the redundancy.
+        // Notably, `align` was found to have no effect when applied only to <table>.
+
+        let table_fmt = concat!(
+            " border=\"1\"",
+            " cellborder=\"1\"",
+            " cellspacing=\"0\"",
+            " cellpadding=\"3\"",
+            " sides=\"rb\"",
+        );
+        write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?;
 
         // A + B: Block header
         if self.state_formatter.column_names().is_empty() {
@@ -186,7 +195,7 @@ where
         // C: Entry state
         self.bg = Background::Light;
         self.results.seek_to_block_start(block);
-        self.write_row_with_full_state(w, "", "(on_entry)")?;
+        self.write_row_with_full_state(w, "", "(on entry)")?;
 
         // D: Statement transfer functions
         for (i, statement) in body[block].statements.iter().enumerate() {
@@ -212,7 +221,7 @@ where
             self.write_row(w, "", "(on successful return)", |this, w, fmt| {
                 write!(
                     w,
-                    r#"<td colspan="{colspan}" {fmt} align="left">"#,
+                    r#"<td balign="left" colspan="{colspan}" {fmt} align="left">"#,
                     colspan = num_state_columns,
                     fmt = fmt,
                 )?;
@@ -311,7 +320,9 @@ where
         f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>,
     ) -> io::Result<()> {
         let bg = self.toggle_background();
-        let fmt = format!("sides=\"tl\" {}", bg.attr());
+        let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" };
+
+        let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr());
 
         write!(
             w,
@@ -345,7 +356,7 @@ where
                 colspan = this.num_state_columns(),
                 fmt = fmt,
             )?;
-            pretty_print_state_elems(w, analysis, state.iter(), ",", LIMIT_40_ALIGN_1)?;
+            pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?;
             write!(w, "}}</td>")
         })
     }
@@ -387,7 +398,6 @@ pub struct SimpleDiff<T: Idx> {
 }
 
 impl<T: Idx> SimpleDiff<T> {
-    #![allow(unused)]
     pub fn new(bits_per_block: usize) -> Self {
         SimpleDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
     }
@@ -417,8 +427,8 @@ where
         }
 
         self.prev_loc = location;
-        write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
-        results.seek_before(location);
+        write!(w, r#"<td {fmt} balign="left" align="left">"#, fmt = fmt)?;
+        results.seek_after(location);
         let curr_state = results.get();
         write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
         self.prev_state.overwrite(curr_state);
@@ -434,7 +444,6 @@ pub struct TwoPhaseDiff<T: Idx> {
 }
 
 impl<T: Idx> TwoPhaseDiff<T> {
-    #![allow(unused)]
     pub fn new(bits_per_block: usize) -> Self {
         TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
     }
@@ -445,7 +454,7 @@ where
     A: Analysis<'tcx>,
 {
     fn column_names(&self) -> &[&str] {
-        &["ENTRY", " EXIT"]
+        &["BEFORE", " AFTER"]
     }
 
     fn write_state_for_location(
@@ -465,7 +474,7 @@ where
 
         self.prev_loc = location;
 
-        // Entry
+        // Before
 
         write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
         results.seek_before(location);
@@ -474,7 +483,7 @@ where
         self.prev_state.overwrite(curr_state);
         write!(w, "</td>")?;
 
-        // Exit
+        // After
 
         write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
         results.seek_after(location);
@@ -492,7 +501,6 @@ pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
 }
 
 impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
-    #![allow(unused)]
     pub fn new(
         body: &'mir mir::Body<'tcx>,
         trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
@@ -527,12 +535,12 @@ where
         for set in &[&block_trans.gen, &block_trans.kill] {
             write!(
                 w,
-                r#"<td {fmt} rowspan="{rowspan}" align="center">"#,
+                r#"<td {fmt} rowspan="{rowspan}" balign="left" align="left">"#,
                 fmt = fmt,
                 rowspan = rowspan
             )?;
 
-            pretty_print_state_elems(&mut w, results.analysis(), set.iter(), "\n", None)?;
+            pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?;
             write!(w, "</td>")?;
         }
 
@@ -564,25 +572,28 @@ fn write_diff<A: Analysis<'tcx>>(
 
     if !set.is_empty() {
         write!(w, r#"<font color="darkgreen">+"#)?;
-        pretty_print_state_elems(w, analysis, set.iter(), ",", LIMIT_40_ALIGN_1)?;
+        pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?;
         write!(w, r#"</font>"#)?;
     }
 
     if !set.is_empty() && !clear.is_empty() {
-        write!(w, "<br/>")?;
+        write!(w, "{}", BR_LEFT)?;
     }
 
     if !clear.is_empty() {
         write!(w, r#"<font color="red">-"#)?;
-        pretty_print_state_elems(w, analysis, clear.iter(), ",", LIMIT_40_ALIGN_1)?;
+        pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?;
         write!(w, r#"</font>"#)?;
     }
 
     Ok(())
 }
 
+const BR_LEFT: &'static str = r#"<br align="left"/>"#;
+const BR_LEFT_SPACE: &'static str = r#"<br align="left"/> "#;
+
 /// Line break policy that breaks at 40 characters and starts the next line with a single space.
-const LIMIT_40_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: "<br/> ", limit: 40 });
+const LIMIT_30_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 });
 
 struct LineBreak {
     sequence: &'static str,
@@ -613,13 +624,6 @@ where
     let mut line_break_inserted = false;
 
     for idx in elems {
-        if first {
-            first = false;
-        } else {
-            write!(w, "{}", sep)?;
-            curr_line_width += sep_width;
-        }
-
         buf.clear();
         analysis.pretty_print_idx(&mut buf, idx)?;
         let idx_str =
@@ -627,11 +631,18 @@ where
         let escaped = dot::escape_html(idx_str);
         let escaped_width = escaped.chars().count();
 
-        if let Some(line_break) = &line_break {
-            if curr_line_width + sep_width + escaped_width > line_break.limit {
-                write!(w, "{}", line_break.sequence)?;
-                line_break_inserted = true;
-                curr_line_width = 0;
+        if first {
+            first = false;
+        } else {
+            write!(w, "{}", sep)?;
+            curr_line_width += sep_width;
+
+            if let Some(line_break) = &line_break {
+                if curr_line_width + sep_width + escaped_width > line_break.limit {
+                    write!(w, "{}", line_break.sequence)?;
+                    line_break_inserted = true;
+                    curr_line_width = 0;
+                }
             }
         }
 
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index 825607a2348..7131eb1144e 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -95,8 +95,6 @@ enum PrevTokenKind {
     Other,
 }
 
-// NOTE: `Ident`s are handled by `common.rs`.
-
 #[derive(Clone)]
 pub struct Parser<'a> {
     pub sess: &'a ParseSess,
@@ -104,14 +102,25 @@ pub struct Parser<'a> {
     /// "Normalized" means that some interpolated tokens
     /// (`$i: ident` and `$l: lifetime` meta-variables) are replaced
     /// with non-interpolated identifier and lifetime tokens they refer to.
-    /// Perhaps the normalized / non-normalized setup can be simplified somehow.
+    /// Use span from this token if you need an isolated span.
     pub token: Token,
-    /// The span of the current non-normalized token.
-    meta_var_span: Option<Span>,
-    /// The span of the previous non-normalized token.
-    pub prev_span: Span,
-    /// The kind of the previous normalized token (in simplified form).
+    /// The current non-normalized token if it's different from `token`.
+    /// Preferable use is through the `unnormalized_token()` getter.
+    /// Use span from this token if you need to concatenate it with some neighbouring spans.
+    unnormalized_token: Option<Token>,
+    /// The previous normalized token.
+    /// Use span from this token if you need an isolated span.
+    prev_token: Token,
+    /// The previous non-normalized token if it's different from `prev_token`.
+    /// Preferable use is through the `unnormalized_prev_token()` getter.
+    /// Use span from this token if you need to concatenate it with some neighbouring spans.
+    unnormalized_prev_token: Option<Token>,
+    /// Equivalent to `prev_token.kind` in simplified form.
+    /// FIXME: Remove in favor of `(unnormalized_)prev_token().kind`.
     prev_token_kind: PrevTokenKind,
+    /// Equivalent to `unnormalized_prev_token().span`.
+    /// FIXME: Remove in favor of `(unnormalized_)prev_token().span`.
+    pub prev_span: Span,
     restrictions: Restrictions,
     /// Used to determine the path to externally loaded source files.
     pub(super) directory: Directory<'a>,
@@ -384,9 +393,11 @@ impl<'a> Parser<'a> {
         let mut parser = Parser {
             sess,
             token: Token::dummy(),
-            prev_span: DUMMY_SP,
-            meta_var_span: None,
+            unnormalized_token: None,
+            prev_token: Token::dummy(),
+            unnormalized_prev_token: None,
             prev_token_kind: PrevTokenKind::Other,
+            prev_span: DUMMY_SP,
             restrictions: Restrictions::empty(),
             recurse_into_file_modules,
             directory: Directory {
@@ -427,6 +438,14 @@ impl<'a> Parser<'a> {
         parser
     }
 
+    fn unnormalized_token(&self) -> &Token {
+        self.unnormalized_token.as_ref().unwrap_or(&self.token)
+    }
+
+    fn unnormalized_prev_token(&self) -> &Token {
+        self.unnormalized_prev_token.as_ref().unwrap_or(&self.prev_token)
+    }
+
     fn next_tok(&mut self) -> Token {
         let mut next = if self.desugar_doc_comments {
             self.token_cursor.next_desugared()
@@ -435,7 +454,7 @@ impl<'a> Parser<'a> {
         };
         if next.span.is_dummy() {
             // Tweak the location for better diagnostics, but keep syntactic context intact.
-            next.span = self.prev_span.with_ctxt(next.span.ctxt());
+            next.span = self.unnormalized_token().span.with_ctxt(next.span.ctxt());
         }
         next
     }
@@ -895,10 +914,13 @@ impl<'a> Parser<'a> {
             self.span_bug(self.token.span, msg);
         }
 
-        self.prev_span = self.meta_var_span.take().unwrap_or(self.token.span);
+        // Update the current and previous tokens.
+        let next_token = self.next_tok();
+        self.prev_token = mem::replace(&mut self.token, next_token);
+        self.unnormalized_prev_token = self.unnormalized_token.take();
 
-        // Record last token kind for possible error recovery.
-        self.prev_token_kind = match self.token.kind {
+        // Update fields derived from the previous token.
+        self.prev_token_kind = match self.prev_token.kind {
             token::DocComment(..) => PrevTokenKind::DocComment,
             token::Comma => PrevTokenKind::Comma,
             token::BinOp(token::Plus) => PrevTokenKind::Plus,
@@ -908,8 +930,8 @@ impl<'a> Parser<'a> {
             token::Ident(..) => PrevTokenKind::Ident,
             _ => PrevTokenKind::Other,
         };
+        self.prev_span = self.unnormalized_prev_token().span;
 
-        self.token = self.next_tok();
         self.expected_tokens.clear();
         // Check after each token.
         self.process_potential_macro_variable();
@@ -917,13 +939,19 @@ impl<'a> Parser<'a> {
 
     /// Advances the parser using provided token as a next one. Use this when
     /// consuming a part of a token. For example a single `<` from `<<`.
+    /// FIXME: this function sets the previous token data to some semi-nonsensical values
+    /// which kind of work because they are currently used in very limited ways in practice.
+    /// Correct token kinds and spans need to be calculated instead.
     fn bump_with(&mut self, next: TokenKind, span: Span) {
-        self.prev_span = self.token.span.with_hi(span.lo());
-        // It would be incorrect to record the kind of the current token, but
-        // fortunately for tokens currently using `bump_with`, the
-        // `prev_token_kind` will be of no use anyway.
+        // Update the current and previous tokens.
+        let next_token = Token::new(next, span);
+        self.prev_token = mem::replace(&mut self.token, next_token);
+        self.unnormalized_prev_token = self.unnormalized_token.take();
+
+        // Update fields derived from the previous token.
         self.prev_token_kind = PrevTokenKind::Other;
-        self.token = Token::new(next, span);
+        self.prev_span = self.unnormalized_prev_token().span.with_hi(span.lo());
+
         self.expected_tokens.clear();
     }
 
@@ -1054,7 +1082,7 @@ impl<'a> Parser<'a> {
     }
 
     pub fn process_potential_macro_variable(&mut self) {
-        self.token = match self.token.kind {
+        let normalized_token = match self.token.kind {
             token::Dollar
                 if self.token.span.from_expansion() && self.look_ahead(1, |t| t.is_ident()) =>
             {
@@ -1071,7 +1099,6 @@ impl<'a> Parser<'a> {
                 return;
             }
             token::Interpolated(ref nt) => {
-                self.meta_var_span = Some(self.token.span);
                 // Interpolated identifier and lifetime tokens are replaced with usual identifier
                 // and lifetime tokens, so the former are never encountered during normal parsing.
                 match **nt {
@@ -1084,6 +1111,7 @@ impl<'a> Parser<'a> {
             }
             _ => return,
         };
+        self.unnormalized_token = Some(mem::replace(&mut self.token, normalized_token));
     }
 
     /// Parses a single token tree from the input.
@@ -1100,7 +1128,7 @@ impl<'a> Parser<'a> {
             }
             token::CloseDelim(_) | token::Eof => unreachable!(),
             _ => {
-                let token = self.token.take();
+                let token = self.token.clone();
                 self.bump();
                 TokenTree::Token(token)
             }
diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs
index e07b0733739..985185230f2 100644
--- a/src/librustc_parse/parser/pat.rs
+++ b/src/librustc_parse/parser/pat.rs
@@ -542,11 +542,14 @@ impl<'a> Parser<'a> {
             }
 
             fn visit_pat(&mut self, pat: &mut P<Pat>) {
-                if let PatKind::Ident(BindingMode::ByValue(ref mut m @ Mutability::Not), ..) =
-                    pat.kind
-                {
-                    *m = Mutability::Mut;
+                if let PatKind::Ident(ref mut bm, ..) = pat.kind {
+                    if let BindingMode::ByValue(ref mut m @ Mutability::Not) = bm {
+                        *m = Mutability::Mut;
+                    }
                     self.0 = true;
+                    // Don't recurse into the subpattern, mut on the outer
+                    // binding doesn't affect the inner bindings.
+                    return;
                 }
                 noop_visit_pat(pat, self);
             }
diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs
index cb14ffb4bd0..761c06b70ee 100644
--- a/src/librustc_parse/parser/path.rs
+++ b/src/librustc_parse/parser/path.rs
@@ -134,7 +134,7 @@ impl<'a> Parser<'a> {
             path
         });
 
-        let lo = self.meta_var_span.unwrap_or(self.token.span);
+        let lo = self.unnormalized_token().span;
         let mut segments = Vec::new();
         let mod_sep_ctxt = self.token.span.ctxt();
         if self.eat(&token::ModSep) {
diff --git a/src/librustc_traits/chalk_context/resolvent_ops.rs b/src/librustc_traits/chalk_context/resolvent_ops.rs
index 301ebf8adc5..dc6018e80ea 100644
--- a/src/librustc_traits/chalk_context/resolvent_ops.rs
+++ b/src/librustc_traits/chalk_context/resolvent_ops.rs
@@ -226,7 +226,9 @@ impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> {
         let b = match b {
             &ty::ReVar(vid) => self
                 .infcx
-                .borrow_region_constraints()
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
                 .opportunistic_resolve_var(self.infcx.tcx, vid),
 
             other => other,
diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs
index 79a006a898a..1970b1e5c5d 100644
--- a/src/librustc_typeck/coherence/builtin.rs
+++ b/src/librustc_typeck/coherence/builtin.rs
@@ -18,14 +18,12 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::ItemKind;
 
 pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
+    let lang_items = tcx.lang_items();
     Checker { tcx, trait_def_id }
-        .check(tcx.lang_items().drop_trait(), visit_implementation_of_drop)
-        .check(tcx.lang_items().copy_trait(), visit_implementation_of_copy)
-        .check(tcx.lang_items().coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
-        .check(
-            tcx.lang_items().dispatch_from_dyn_trait(),
-            visit_implementation_of_dispatch_from_dyn,
-        );
+        .check(lang_items.drop_trait(), visit_implementation_of_drop)
+        .check(lang_items.copy_trait(), visit_implementation_of_copy)
+        .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
+        .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn);
 }
 
 struct Checker<'tcx> {
diff --git a/src/librustc_typeck/coherence/inherent_impls_overlap.rs b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
index fb9c173f520..ffea849c4f2 100644
--- a/src/librustc_typeck/coherence/inherent_impls_overlap.rs
+++ b/src/librustc_typeck/coherence/inherent_impls_overlap.rs
@@ -1,5 +1,5 @@
 use crate::namespace::Namespace;
-use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
+use rustc::traits::{self, SkipLeakCheck};
 use rustc::ty::{AssocItem, TyCtxt};
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
@@ -93,7 +93,6 @@ impl InherentOverlapChecker<'tcx> {
             self.tcx,
             impl1_def_id,
             impl2_def_id,
-            IntercrateMode::Issue43355,
             // We go ahead and just skip the leak check for
             // inherent impls without warning.
             SkipLeakCheck::Yes,
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 5583e3418b2..1526182576c 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -10,7 +10,7 @@ use rustc::ty::query::Providers;
 use rustc::ty::{self, TyCtxt, TypeFoldable};
 use rustc_errors::struct_span_err;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::HirId;
+use rustc_span::Span;
 
 mod builtin;
 mod inherent_impls;
@@ -18,37 +18,35 @@ mod inherent_impls_overlap;
 mod orphan;
 mod unsafety;
 
-fn check_impl(tcx: TyCtxt<'_>, hir_id: HirId) {
-    let impl_def_id = tcx.hir().local_def_id(hir_id);
-
-    // If there are no traits, then this implementation must have a
-    // base type.
-
-    if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) {
-        debug!(
-            "(checking implementation) adding impl for trait '{:?}', item '{}'",
-            trait_ref,
-            tcx.def_path_str(impl_def_id)
-        );
+/// Obtains the span of just the impl header of `impl_def_id`.
+fn impl_header_span(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Span {
+    tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap())
+}
 
-        // Skip impls where one of the self type is an error type.
-        // This occurs with e.g., resolve failures (#30589).
-        if trait_ref.references_error() {
-            return;
-        }
+fn check_impl(tcx: TyCtxt<'_>, impl_def_id: DefId, trait_ref: ty::TraitRef<'_>) {
+    debug!(
+        "(checking implementation) adding impl for trait '{:?}', item '{}'",
+        trait_ref,
+        tcx.def_path_str(impl_def_id)
+    );
 
-        enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id);
-        enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id);
+    // Skip impls where one of the self type is an error type.
+    // This occurs with e.g., resolve failures (#30589).
+    if trait_ref.references_error() {
+        return;
     }
+
+    enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id);
+    enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id);
 }
 
 fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, trait_def_id: DefId) {
     let did = Some(trait_def_id);
     let li = tcx.lang_items();
-    let span = tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
 
     // Disallow *all* explicit impls of `Sized` and `Unsize` for now.
     if did == li.sized_trait() {
+        let span = impl_header_span(tcx, impl_def_id);
         struct_span_err!(
             tcx.sess,
             span,
@@ -61,6 +59,7 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, tra
     }
 
     if did == li.unsize_trait() {
+        let span = impl_header_span(tcx, impl_def_id);
         struct_span_err!(
             tcx.sess,
             span,
@@ -86,6 +85,8 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, tra
     } else {
         return; // everything OK
     };
+
+    let span = impl_header_span(tcx, impl_def_id);
     struct_span_err!(
         tcx.sess,
         span,
@@ -109,7 +110,7 @@ fn enforce_empty_impls_for_marker_traits(tcx: TyCtxt<'_>, impl_def_id: DefId, tr
         return;
     }
 
-    let span = tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
+    let span = impl_header_span(tcx, impl_def_id);
     struct_span_err!(tcx.sess, span, E0715, "impls for marker traits cannot contain items").emit();
 }
 
@@ -129,12 +130,17 @@ pub fn provide(providers: &mut Providers<'_>) {
 }
 
 fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) {
+    // Trigger building the specialization graph for the trait. This will detect and report any
+    // overlap errors.
+    tcx.specialization_graph_of(def_id);
+
     let impls = tcx.hir().trait_impls(def_id);
-    for &impl_id in impls {
-        check_impl(tcx, impl_id);
-    }
-    for &impl_id in impls {
-        check_impl_overlap(tcx, impl_id);
+    for &hir_id in impls {
+        let impl_def_id = tcx.hir().local_def_id(hir_id);
+        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+
+        check_impl(tcx, impl_def_id, trait_ref);
+        check_object_overlap(tcx, impl_def_id, trait_ref);
     }
     builtin::check_trait(tcx, def_id);
 }
@@ -152,12 +158,12 @@ pub fn check_coherence(tcx: TyCtxt<'_>) {
     tcx.ensure().crate_inherent_impls_overlap_check(LOCAL_CRATE);
 }
 
-/// Overlap: no two impls for the same trait are implemented for the
-/// same type. Likewise, no two inherent impls for a given type
-/// constructor provide a method with the same name.
-fn check_impl_overlap<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId) {
-    let impl_def_id = tcx.hir().local_def_id(hir_id);
-    let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+/// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`.
+fn check_object_overlap<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    impl_def_id: DefId,
+    trait_ref: ty::TraitRef<'tcx>,
+) {
     let trait_def_id = trait_ref.def_id;
 
     if trait_ref.references_error() {
@@ -165,11 +171,7 @@ fn check_impl_overlap<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId) {
         return;
     }
 
-    // Trigger building the specialization graph for the trait of this impl.
-    // This will detect any overlap errors.
-    tcx.specialization_graph_of(trait_def_id);
-
-    // check for overlap with the automatic `impl Trait for Trait`
+    // check for overlap with the automatic `impl Trait for dyn Trait`
     if let ty::Dynamic(ref data, ..) = trait_ref.self_ty().kind {
         // This is something like impl Trait1 for Trait2. Illegal
         // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
@@ -194,17 +196,17 @@ fn check_impl_overlap<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId) {
             } else {
                 let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id);
                 if supertrait_def_ids.any(|d| d == trait_def_id) {
-                    let sp = tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
+                    let span = impl_header_span(tcx, impl_def_id);
                     struct_span_err!(
                         tcx.sess,
-                        sp,
+                        span,
                         E0371,
                         "the object type `{}` automatically implements the trait `{}`",
                         trait_ref.self_ty(),
                         tcx.def_path_str(trait_def_id)
                     )
                     .span_label(
-                        sp,
+                        span,
                         format!(
                             "`{}` automatically implements trait `{}`",
                             trait_ref.self_ty(),
diff --git a/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr b/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr
index e5cc298a435..71f997c54c6 100644
--- a/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr
+++ b/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr
@@ -1,10 +1,10 @@
-error[E0391]: cycle detected when processing `Trait`
+error[E0391]: cycle detected when building specialization graph of trait `Trait`
   --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:8:1
    |
 LL | trait Trait<T> { type Assoc; }
    | ^^^^^^^^^^^^^^
    |
-   = note: ...which again requires processing `Trait`, completing the cycle
+   = note: ...which again requires building specialization graph of trait `Trait`, completing the cycle
 note: cycle used when coherence checking all impls of trait `Trait`
   --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:8:1
    |
diff --git a/src/test/ui/consts/const-nonzero.rs b/src/test/ui/consts/const-nonzero.rs
index 6db3d1b3331..2160bad4807 100644
--- a/src/test/ui/consts/const-nonzero.rs
+++ b/src/test/ui/consts/const-nonzero.rs
@@ -1,9 +1,18 @@
-// build-pass (FIXME(62277): could be check-pass?)
+// run-pass
+
+#![feature(const_nonzero_int_methods)]
 
 use std::num::NonZeroU8;
 
 const X: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(5) };
 const Y: u8 = X.get();
 
+const ZERO: Option<NonZeroU8> = NonZeroU8::new(0);
+const ONE: Option<NonZeroU8> = NonZeroU8::new(1);
+
 fn main() {
+    assert_eq!(Y, 5);
+
+    assert!(ZERO.is_none());
+    assert_eq!(ONE.unwrap().get(), 1);
 }
diff --git a/src/test/ui/parser/mbe_missing_right_paren.stderr b/src/test/ui/parser/mbe_missing_right_paren.stderr
index 4ce39de8866..ec04b937b9a 100644
--- a/src/test/ui/parser/mbe_missing_right_paren.stderr
+++ b/src/test/ui/parser/mbe_missing_right_paren.stderr
@@ -22,10 +22,10 @@ LL | macro_rules! abc(ؼ;
    |                   ^
 
 error: unexpected end of macro invocation
-  --> $DIR/mbe_missing_right_paren.rs:3:1
+  --> $DIR/mbe_missing_right_paren.rs:3:19
    |
 LL | macro_rules! abc(ؼ
-   | ^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments
+   |                   ^ missing tokens in macro arguments
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs b/src/test/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs
new file mode 100644
index 00000000000..497d94a3db0
--- /dev/null
+++ b/src/test/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs
@@ -0,0 +1,13 @@
+// check-pass
+
+#![feature(bindings_after_at)]
+#![deny(unused_mut)]
+
+fn main() {
+    let mut is_mut @ not_mut = 42;
+    &mut is_mut;
+    &not_mut;
+    let not_mut @ mut is_mut = 42;
+    &mut is_mut;
+    &not_mut;
+}
diff --git a/src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs
new file mode 100644
index 00000000000..54f04117f7d
--- /dev/null
+++ b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs
@@ -0,0 +1,13 @@
+#![feature(bindings_after_at)]
+
+fn main() {
+    let mut is_mut @ not_mut = 42;
+    &mut is_mut;
+    &mut not_mut;
+    //~^ ERROR cannot borrow
+
+    let not_mut @ mut is_mut = 42;
+    &mut is_mut;
+    &mut not_mut;
+    //~^ ERROR cannot borrow
+}
diff --git a/src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr
new file mode 100644
index 00000000000..a8d5e4c4c69
--- /dev/null
+++ b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr
@@ -0,0 +1,21 @@
+error[E0596]: cannot borrow `not_mut` as mutable, as it is not declared as mutable
+  --> $DIR/nested-binding-modes-mut.rs:6:5
+   |
+LL |     let mut is_mut @ not_mut = 42;
+   |                      ------- help: consider changing this to be mutable: `mut not_mut`
+LL |     &mut is_mut;
+LL |     &mut not_mut;
+   |     ^^^^^^^^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow `not_mut` as mutable, as it is not declared as mutable
+  --> $DIR/nested-binding-modes-mut.rs:11:5
+   |
+LL |     let not_mut @ mut is_mut = 42;
+   |         -------------------- help: consider changing this to be mutable: `mut not_mut`
+LL |     &mut is_mut;
+LL |     &mut not_mut;
+   |     ^^^^^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs
new file mode 100644
index 00000000000..d5086aec93e
--- /dev/null
+++ b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs
@@ -0,0 +1,13 @@
+#![feature(bindings_after_at)]
+
+fn main() {
+    let ref is_ref @ is_val = 42;
+    *is_ref;
+    *is_val;
+    //~^ ERROR cannot be dereferenced
+
+    let is_val @ ref is_ref = 42;
+    *is_ref;
+    *is_val;
+    //~^ ERROR cannot be dereferenced
+}
diff --git a/src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr
new file mode 100644
index 00000000000..9cc928d2149
--- /dev/null
+++ b/src/test/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr
@@ -0,0 +1,15 @@
+error[E0614]: type `{integer}` cannot be dereferenced
+  --> $DIR/nested-binding-modes-ref.rs:6:5
+   |
+LL |     *is_val;
+   |     ^^^^^^^
+
+error[E0614]: type `{integer}` cannot be dereferenced
+  --> $DIR/nested-binding-modes-ref.rs:11:5
+   |
+LL |     *is_val;
+   |     ^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0614`.