about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-06-06 19:12:05 +0000
committerbors <bors@rust-lang.org>2021-06-06 19:12:05 +0000
commit35fff69d043b1c0f5c29894e7f4b0da8b039c131 (patch)
tree91f874db12fbb6a761339cff9451ee5d83889d64
parent5b638c1d3751b7ab31cac9739add516bdf39e10a (diff)
parentfad2242ff7bbafed5e7398e1286c3935b31f8233 (diff)
downloadrust-35fff69d043b1c0f5c29894e7f4b0da8b039c131.tar.gz
rust-35fff69d043b1c0f5c29894e7f4b0da8b039c131.zip
Auto merge of #85343 - Aaron1011:variance-diag, r=estebank
Add variance-related information to lifetime error messages

This PR adds a basic framework for displaying variance-related information in error messages. For example:

```
error: lifetime may not live long enough
  --> $DIR/type-check-pointer-comparisons.rs:12:5
   |
LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) {
   |                --  -- lifetime `'b` defined here
   |                |
   |                lifetime `'a` defined here
LL |     x == y;
   |     ^ requires that `'a` must outlive `'b`
   |
   = help: consider adding the following bound: `'a: 'b`
   = note: requirement occurs because of a mutable pointer to &i32
   = note: mutable pointers are invariant over their type parameter
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
```

The last three lines are new.

This is accomplished by adding a new struct `VarianceDiagInfo`, and passing it along through the various relation methods. When relating types that change the variance (e.g. `&mut T` or `*mut T`), we pass a more specific `VarianceDiagInfo` storing information about the cause of the variance change. When an error, we use the `VarianceDiagInfo` to add additional information to the error message.

This PR doesn't change any variance-related computation or behavior - only diagnostic messages. Therefore, the implementation is quite incomplete - more detailed error messages can be filled in in subsequent PRs.

Limitations:
* We only attempt to deal with invariance - since it's at the bottom of the 'variance lattice', our variance will never change again after it becomes invariant. Handling contravariance would be trickier, since we can change between contravariance and covariance multiple times (e.g. `fn(fn(&'static u8))`). Since contravariance (AFAIK) is only used for function arguments, we can probably get away without a very fancy message for cases involving contravariance.
* `VarianceDiagInfo` currently only handles mutable pointers/references. However, user-defined types (structs, enums, and unions) have the variance of their type parameters inferred, so it would be good to eventually display information about that. We'll want to try to find a balance between displaying too much and too little information about how the variance was inferred.
* The improved error messages are only displayed when `#![feature(nll)]` / `-Z borrowck=mir` is enabled.  If issue https://github.com/rust-lang/rust/issues/58781 is not resolved relatively soon, then we might want to duplicate some of this logic in the 'current' (non-NLL) region/outlives handling code.
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs7
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs25
-rw-r--r--compiler/rustc_infer/src/infer/equate.rs1
-rw-r--r--compiler/rustc_infer/src/infer/glb.rs3
-rw-r--r--compiler/rustc_infer/src/infer/lub.rs3
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs34
-rw-r--r--compiler/rustc_infer/src/infer/sub.rs1
-rw-r--r--compiler/rustc_middle/src/ty/_match.rs1
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs85
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs52
-rw-r--r--compiler/rustc_mir/src/borrow_check/constraints/graph.rs68
-rw-r--r--compiler/rustc_mir/src/borrow_check/constraints/mod.rs31
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs14
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs20
-rw-r--r--compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs12
-rw-r--r--compiler/rustc_mir/src/borrow_check/region_infer/mod.rs107
-rw-r--r--compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs1
-rw-r--r--compiler/rustc_mir/src/borrow_check/type_check/liveness/mod.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/type_check/mod.rs7
-rw-r--r--compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs8
-rw-r--r--compiler/rustc_mir/src/lib.rs1
-rw-r--r--compiler/rustc_typeck/src/check/dropck.rs1
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-4.stderr8
-rw-r--r--src/test/ui/match/match-ref-mut-invariance.nll.stderr3
-rw-r--r--src/test/ui/match/match-ref-mut-let-invariance.nll.stderr3
-rw-r--r--src/test/ui/nll/type-check-pointer-coercions.stderr6
-rw-r--r--src/test/ui/nll/type-check-pointer-comparisons.stderr18
-rw-r--r--src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr3
-rw-r--r--src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr3
-rw-r--r--src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr3
-rw-r--r--src/test/ui/regions/regions-trait-object-subtyping.nll.stderr6
33 files changed, 397 insertions, 144 deletions
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index b8ecc949588..c3c28d70081 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -660,7 +660,12 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
         )
     }
 
-    fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
+    fn push_outlives(
+        &mut self,
+        sup: ty::Region<'tcx>,
+        sub: ty::Region<'tcx>,
+        _info: ty::VarianceDiagInfo<'tcx>,
+    ) {
         self.obligations.push(Obligation {
             cause: self.cause.clone(),
             param_env: self.param_env,
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 30214e94203..3a11b5a2144 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -371,9 +371,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         match dir {
             EqTo => self.equate(a_is_expected).relate(a_ty, b_ty),
             SubtypeOf => self.sub(a_is_expected).relate(a_ty, b_ty),
-            SupertypeOf => {
-                self.sub(a_is_expected).relate_with_variance(ty::Contravariant, a_ty, b_ty)
-            }
+            SupertypeOf => self.sub(a_is_expected).relate_with_variance(
+                ty::Contravariant,
+                ty::VarianceDiagInfo::default(),
+                a_ty,
+                b_ty,
+            ),
         }?;
 
         Ok(())
@@ -574,6 +577,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -737,7 +741,12 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                 if self.tcx().lazy_normalization() =>
             {
                 assert_eq!(promoted, None);
-                let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?;
+                let substs = self.relate_with_variance(
+                    ty::Variance::Invariant,
+                    ty::VarianceDiagInfo::default(),
+                    substs,
+                    substs,
+                )?;
                 Ok(self.tcx().mk_const(ty::Const {
                     ty: c.ty,
                     val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
@@ -831,6 +840,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         _variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -965,7 +975,12 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
                 if self.tcx().lazy_normalization() =>
             {
                 assert_eq!(promoted, None);
-                let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?;
+                let substs = self.relate_with_variance(
+                    ty::Variance::Invariant,
+                    ty::VarianceDiagInfo::default(),
+                    substs,
+                    substs,
+                )?;
                 Ok(self.tcx().mk_const(ty::Const {
                     ty: c.ty,
                     val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs
index 45ba50bb634..0c93271a1ae 100644
--- a/compiler/rustc_infer/src/infer/equate.rs
+++ b/compiler/rustc_infer/src/infer/equate.rs
@@ -59,6 +59,7 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         _: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs
index 02662043dba..60f02b84aa3 100644
--- a/compiler/rustc_infer/src/infer/glb.rs
+++ b/compiler/rustc_infer/src/infer/glb.rs
@@ -43,6 +43,7 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -96,7 +97,7 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
         // When higher-ranked types are involved, computing the LUB is
         // very challenging, switch to invariance. This is obviously
         // overly conservative but works ok in practice.
-        self.relate_with_variance(ty::Variance::Invariant, a, b)?;
+        self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
         Ok(a)
     }
 }
diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs
index 4fa8f2f1a6a..a08323535c5 100644
--- a/compiler/rustc_infer/src/infer/lub.rs
+++ b/compiler/rustc_infer/src/infer/lub.rs
@@ -43,6 +43,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -96,7 +97,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
         // When higher-ranked types are involved, computing the LUB is
         // very challenging, switch to invariance. This is obviously
         // overly conservative but works ok in practice.
-        self.relate_with_variance(ty::Variance::Invariant, a, b)?;
+        self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
         Ok(a)
     }
 }
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index 077d2cc20a2..20be06adfd0 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -55,6 +55,8 @@ where
     /// - Bivariant means that it doesn't matter.
     ambient_variance: ty::Variance,
 
+    ambient_variance_info: ty::VarianceDiagInfo<'tcx>,
+
     /// When we pass through a set of binders (e.g., when looking into
     /// a `fn` type), we push a new bound region scope onto here. This
     /// will contain the instantiated region for each region in those
@@ -78,7 +80,12 @@ pub trait TypeRelatingDelegate<'tcx> {
     /// satisfied for the two types to be related. `sub` and `sup` may
     /// be regions from the type or new variables created through the
     /// delegate.
-    fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
+    fn push_outlives(
+        &mut self,
+        sup: ty::Region<'tcx>,
+        sub: ty::Region<'tcx>,
+        info: ty::VarianceDiagInfo<'tcx>,
+    );
 
     fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
 
@@ -138,7 +145,14 @@ where
         delegate: D,
         ambient_variance: ty::Variance,
     ) -> Self {
-        Self { infcx, delegate, ambient_variance, a_scopes: vec![], b_scopes: vec![] }
+        Self {
+            infcx,
+            delegate,
+            ambient_variance,
+            ambient_variance_info: ty::VarianceDiagInfo::default(),
+            a_scopes: vec![],
+            b_scopes: vec![],
+        }
     }
 
     fn ambient_covariance(&self) -> bool {
@@ -239,10 +253,15 @@ where
 
     /// Push a new outlives requirement into our output set of
     /// constraints.
-    fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
+    fn push_outlives(
+        &mut self,
+        sup: ty::Region<'tcx>,
+        sub: ty::Region<'tcx>,
+        info: ty::VarianceDiagInfo<'tcx>,
+    ) {
         debug!("push_outlives({:?}: {:?})", sup, sub);
 
-        self.delegate.push_outlives(sup, sub);
+        self.delegate.push_outlives(sup, sub, info);
     }
 
     /// Relate a projection type and some value type lazily. This will always
@@ -490,6 +509,7 @@ where
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
+        info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -497,6 +517,7 @@ where
 
         let old_ambient_variance = self.ambient_variance;
         self.ambient_variance = self.ambient_variance.xform(variance);
+        self.ambient_variance_info = self.ambient_variance_info.clone().xform(info);
 
         debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance);
 
@@ -574,12 +595,12 @@ where
 
         if self.ambient_covariance() {
             // Covariance: a <= b. Hence, `b: a`.
-            self.push_outlives(v_b, v_a);
+            self.push_outlives(v_b, v_a, self.ambient_variance_info.clone());
         }
 
         if self.ambient_contravariance() {
             // Contravariant: b <= a. Hence, `a: b`.
-            self.push_outlives(v_a, v_b);
+            self.push_outlives(v_a, v_b, self.ambient_variance_info.clone());
         }
 
         Ok(a)
@@ -835,6 +856,7 @@ where
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs
index bf5f328233d..b3131936ae0 100644
--- a/compiler/rustc_infer/src/infer/sub.rs
+++ b/compiler/rustc_infer/src/infer/sub.rs
@@ -62,6 +62,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs
index 8e2c79701af..02ff1b9f4d6 100644
--- a/compiler/rustc_middle/src/ty/_match.rs
+++ b/compiler/rustc_middle/src/ty/_match.rs
@@ -46,6 +46,7 @@ impl TypeRelation<'tcx> for Match<'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         _: ty::Variance,
+        _: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 5da2aab4093..b58c92b8415 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -71,7 +71,7 @@ pub use self::sty::{
     ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig,
     GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection,
     PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind,
-    RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts,
+    RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind,
 };
 pub use self::trait_def::TraitDef;
 
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index b6f93c9bd59..872d12cac93 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -67,6 +67,7 @@ pub trait TypeRelation<'tcx>: Sized {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
+        info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T>;
@@ -111,24 +112,23 @@ pub trait Relate<'tcx>: TypeFoldable<'tcx> + Copy {
 ///////////////////////////////////////////////////////////////////////////
 // Relate impls
 
-impl<'tcx> Relate<'tcx> for ty::TypeAndMut<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::TypeAndMut<'tcx>,
-        b: ty::TypeAndMut<'tcx>,
-    ) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> {
-        debug!("{}.mts({:?}, {:?})", relation.tag(), a, b);
-        if a.mutbl != b.mutbl {
-            Err(TypeError::Mutability)
-        } else {
-            let mutbl = a.mutbl;
-            let variance = match mutbl {
-                ast::Mutability::Not => ty::Covariant,
-                ast::Mutability::Mut => ty::Invariant,
-            };
-            let ty = relation.relate_with_variance(variance, a.ty, b.ty)?;
-            Ok(ty::TypeAndMut { ty, mutbl })
-        }
+fn relate_type_and_mut<'tcx, R: TypeRelation<'tcx>>(
+    relation: &mut R,
+    a: ty::TypeAndMut<'tcx>,
+    b: ty::TypeAndMut<'tcx>,
+    kind: ty::VarianceDiagMutKind,
+) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> {
+    debug!("{}.mts({:?}, {:?})", relation.tag(), a, b);
+    if a.mutbl != b.mutbl {
+        Err(TypeError::Mutability)
+    } else {
+        let mutbl = a.mutbl;
+        let (variance, info) = match mutbl {
+            ast::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None),
+            ast::Mutability::Mut => (ty::Invariant, ty::VarianceDiagInfo::Mut { kind, ty: a.ty }),
+        };
+        let ty = relation.relate_with_variance(variance, info, a.ty, b.ty)?;
+        Ok(ty::TypeAndMut { ty, mutbl })
     }
 }
 
@@ -142,7 +142,7 @@ pub fn relate_substs<R: TypeRelation<'tcx>>(
 
     let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| {
         let variance = variances.map_or(ty::Invariant, |v| v[i]);
-        relation.relate_with_variance(variance, a, b)
+        relation.relate_with_variance(variance, ty::VarianceDiagInfo::default(), a, b)
     });
 
     tcx.mk_substs(params)
@@ -177,7 +177,12 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> {
                 if is_output {
                     relation.relate(a, b)
                 } else {
-                    relation.relate_with_variance(ty::Contravariant, a, b)
+                    relation.relate_with_variance(
+                        ty::Contravariant,
+                        ty::VarianceDiagInfo::default(),
+                        a,
+                        b,
+                    )
                 }
             })
             .enumerate()
@@ -251,8 +256,18 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> {
                 b.item_def_id,
             )))
         } else {
-            let ty = relation.relate_with_variance(ty::Invariant, a.ty, b.ty)?;
-            let substs = relation.relate_with_variance(ty::Invariant, a.substs, b.substs)?;
+            let ty = relation.relate_with_variance(
+                ty::Invariant,
+                ty::VarianceDiagInfo::default(),
+                a.ty,
+                b.ty,
+            )?;
+            let substs = relation.relate_with_variance(
+                ty::Invariant,
+                ty::VarianceDiagInfo::default(),
+                a.substs,
+                b.substs,
+            )?;
             Ok(ty::ExistentialProjection { item_def_id: a.item_def_id, substs, ty })
         }
     }
@@ -364,7 +379,12 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
 
         (&ty::Dynamic(a_obj, a_region), &ty::Dynamic(b_obj, b_region)) => {
             let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| {
-                relation.relate_with_variance(ty::Contravariant, a_region, b_region)
+                relation.relate_with_variance(
+                    ty::Contravariant,
+                    ty::VarianceDiagInfo::default(),
+                    a_region,
+                    b_region,
+                )
             })?;
             Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound))
         }
@@ -398,15 +418,20 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
         }
 
         (&ty::RawPtr(a_mt), &ty::RawPtr(b_mt)) => {
-            let mt = relation.relate(a_mt, b_mt)?;
+            let mt = relate_type_and_mut(relation, a_mt, b_mt, ty::VarianceDiagMutKind::RawPtr)?;
             Ok(tcx.mk_ptr(mt))
         }
 
         (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => {
-            let r = relation.relate_with_variance(ty::Contravariant, a_r, b_r)?;
+            let r = relation.relate_with_variance(
+                ty::Contravariant,
+                ty::VarianceDiagInfo::default(),
+                a_r,
+                b_r,
+            )?;
             let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl };
             let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl };
-            let mt = relation.relate(a_mt, b_mt)?;
+            let mt = relate_type_and_mut(relation, a_mt, b_mt, ty::VarianceDiagMutKind::Ref)?;
             Ok(tcx.mk_ref(r, mt))
         }
 
@@ -536,8 +561,12 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
         (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
             if au.def == bu.def && au.promoted == bu.promoted =>
         {
-            let substs =
-                relation.relate_with_variance(ty::Variance::Invariant, au.substs, bu.substs)?;
+            let substs = relation.relate_with_variance(
+                ty::Variance::Invariant,
+                ty::VarianceDiagInfo::default(),
+                au.substs,
+                bu.substs,
+            )?;
             return Ok(tcx.mk_const(ty::Const {
                 val: ty::ConstKind::Unevaluated(ty::Unevaluated {
                     def: au.def,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index c1f71fbbfa4..1d9ff512288 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2181,3 +2181,55 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 }
+
+/// Extra information about why we ended up with a particular variance.
+/// This is only used to add more information to error messages, and
+/// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo`
+/// may lead to confusing notes in error messages, it will never cause
+/// a miscompilation or unsoundness.
+///
+/// When in doubt, use `VarianceDiagInfo::default()`
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum VarianceDiagInfo<'tcx> {
+    /// No additional information - this is the default.
+    /// We will not add any additional information to error messages.
+    None,
+    /// We switched our variance because a type occurs inside
+    /// the generic argument of a mutable reference or pointer
+    /// (`*mut T` or `&mut T`). In either case, our variance
+    /// will always be `Invariant`.
+    Mut {
+        /// Tracks whether we had a mutable pointer or reference,
+        /// for better error messages
+        kind: VarianceDiagMutKind,
+        /// The type parameter of the mutable pointer/reference
+        /// (the `T` in `&mut T` or `*mut T`).
+        ty: Ty<'tcx>,
+    },
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum VarianceDiagMutKind {
+    /// A mutable raw pointer (`*mut T`)
+    RawPtr,
+    /// A mutable reference (`&mut T`)
+    Ref,
+}
+
+impl<'tcx> VarianceDiagInfo<'tcx> {
+    /// Mirrors `Variance::xform` - used to 'combine' the existing
+    /// and new `VarianceDiagInfo`s when our variance changes.
+    pub fn xform(self, other: VarianceDiagInfo<'tcx>) -> VarianceDiagInfo<'tcx> {
+        // For now, just use the first `VarianceDiagInfo::Mut` that we see
+        match self {
+            VarianceDiagInfo::None => other,
+            VarianceDiagInfo::Mut { .. } => self,
+        }
+    }
+}
+
+impl<'tcx> Default for VarianceDiagInfo<'tcx> {
+    fn default() -> Self {
+        Self::None
+    }
+}
diff --git a/compiler/rustc_mir/src/borrow_check/constraints/graph.rs b/compiler/rustc_mir/src/borrow_check/constraints/graph.rs
index f3f6b8c10da..9e4cfb2cc00 100644
--- a/compiler/rustc_mir/src/borrow_check/constraints/graph.rs
+++ b/compiler/rustc_mir/src/borrow_check/constraints/graph.rs
@@ -1,7 +1,7 @@
 use rustc_data_structures::graph;
 use rustc_index::vec::IndexVec;
 use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::ty::RegionVid;
+use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
 use rustc_span::DUMMY_SP;
 
 use crate::borrow_check::{
@@ -26,8 +26,8 @@ crate type ReverseConstraintGraph = ConstraintGraph<Reverse>;
 /// Marker trait that controls whether a `R1: R2` constraint
 /// represents an edge `R1 -> R2` or `R2 -> R1`.
 crate trait ConstraintGraphDirecton: Copy + 'static {
-    fn start_region(c: &OutlivesConstraint) -> RegionVid;
-    fn end_region(c: &OutlivesConstraint) -> RegionVid;
+    fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid;
+    fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid;
     fn is_normal() -> bool;
 }
 
@@ -39,11 +39,11 @@ crate trait ConstraintGraphDirecton: Copy + 'static {
 crate struct Normal;
 
 impl ConstraintGraphDirecton for Normal {
-    fn start_region(c: &OutlivesConstraint) -> RegionVid {
+    fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
         c.sup
     }
 
-    fn end_region(c: &OutlivesConstraint) -> RegionVid {
+    fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
         c.sub
     }
 
@@ -60,11 +60,11 @@ impl ConstraintGraphDirecton for Normal {
 crate struct Reverse;
 
 impl ConstraintGraphDirecton for Reverse {
-    fn start_region(c: &OutlivesConstraint) -> RegionVid {
+    fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
         c.sub
     }
 
-    fn end_region(c: &OutlivesConstraint) -> RegionVid {
+    fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
         c.sup
     }
 
@@ -78,7 +78,7 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
     /// R2` is treated as an edge `R1 -> R2`. We use this graph to
     /// construct SCCs for region inference but also for error
     /// reporting.
-    crate fn new(direction: D, set: &OutlivesConstraintSet, num_region_vars: usize) -> Self {
+    crate fn new(direction: D, set: &OutlivesConstraintSet<'_>, num_region_vars: usize) -> Self {
         let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars);
         let mut next_constraints = IndexVec::from_elem(None, &set.outlives);
 
@@ -96,21 +96,21 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
     /// Given the constraint set from which this graph was built
     /// creates a region graph so that you can iterate over *regions*
     /// and not constraints.
-    crate fn region_graph<'rg>(
+    crate fn region_graph<'rg, 'tcx>(
         &'rg self,
-        set: &'rg OutlivesConstraintSet,
+        set: &'rg OutlivesConstraintSet<'tcx>,
         static_region: RegionVid,
-    ) -> RegionGraph<'rg, D> {
+    ) -> RegionGraph<'rg, 'tcx, D> {
         RegionGraph::new(set, self, static_region)
     }
 
     /// Given a region `R`, iterate over all constraints `R: R1`.
-    crate fn outgoing_edges<'a>(
+    crate fn outgoing_edges<'a, 'tcx>(
         &'a self,
         region_sup: RegionVid,
-        constraints: &'a OutlivesConstraintSet,
+        constraints: &'a OutlivesConstraintSet<'tcx>,
         static_region: RegionVid,
-    ) -> Edges<'a, D> {
+    ) -> Edges<'a, 'tcx, D> {
         //if this is the `'static` region and the graph's direction is normal,
         //then setup the Edges iterator to return all regions #53178
         if region_sup == static_region && D::is_normal() {
@@ -129,22 +129,22 @@ impl<D: ConstraintGraphDirecton> ConstraintGraph<D> {
     }
 }
 
-crate struct Edges<'s, D: ConstraintGraphDirecton> {
+crate struct Edges<'s, 'tcx, D: ConstraintGraphDirecton> {
     graph: &'s ConstraintGraph<D>,
-    constraints: &'s OutlivesConstraintSet,
+    constraints: &'s OutlivesConstraintSet<'tcx>,
     pointer: Option<OutlivesConstraintIndex>,
     next_static_idx: Option<usize>,
     static_region: RegionVid,
 }
 
-impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
-    type Item = OutlivesConstraint;
+impl<'s, 'tcx, D: ConstraintGraphDirecton> Iterator for Edges<'s, 'tcx, D> {
+    type Item = OutlivesConstraint<'tcx>;
 
     fn next(&mut self) -> Option<Self::Item> {
         if let Some(p) = self.pointer {
             self.pointer = self.graph.next_constraints[p];
 
-            Some(self.constraints[p])
+            Some(self.constraints[p].clone())
         } else if let Some(next_static_idx) = self.next_static_idx {
             self.next_static_idx = if next_static_idx == (self.graph.first_constraints.len() - 1) {
                 None
@@ -157,6 +157,7 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
                 sub: next_static_idx.into(),
                 locations: Locations::All(DUMMY_SP),
                 category: ConstraintCategory::Internal,
+                variance_info: VarianceDiagInfo::default(),
             })
         } else {
             None
@@ -167,19 +168,19 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> {
 /// This struct brings together a constraint set and a (normal, not
 /// reverse) constraint graph. It implements the graph traits and is
 /// usd for doing the SCC computation.
-crate struct RegionGraph<'s, D: ConstraintGraphDirecton> {
-    set: &'s OutlivesConstraintSet,
+crate struct RegionGraph<'s, 'tcx, D: ConstraintGraphDirecton> {
+    set: &'s OutlivesConstraintSet<'tcx>,
     constraint_graph: &'s ConstraintGraph<D>,
     static_region: RegionVid,
 }
 
-impl<'s, D: ConstraintGraphDirecton> RegionGraph<'s, D> {
+impl<'s, 'tcx, D: ConstraintGraphDirecton> RegionGraph<'s, 'tcx, D> {
     /// Creates a "dependency graph" where each region constraint `R1:
     /// R2` is treated as an edge `R1 -> R2`. We use this graph to
     /// construct SCCs for region inference but also for error
     /// reporting.
     crate fn new(
-        set: &'s OutlivesConstraintSet,
+        set: &'s OutlivesConstraintSet<'tcx>,
         constraint_graph: &'s ConstraintGraph<D>,
         static_region: RegionVid,
     ) -> Self {
@@ -188,18 +189,18 @@ impl<'s, D: ConstraintGraphDirecton> RegionGraph<'s, D> {
 
     /// Given a region `R`, iterate over all regions `R1` such that
     /// there exists a constraint `R: R1`.
-    crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'_, D> {
+    crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'_, 'tcx, D> {
         Successors {
             edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
         }
     }
 }
 
-crate struct Successors<'s, D: ConstraintGraphDirecton> {
-    edges: Edges<'s, D>,
+crate struct Successors<'s, 'tcx, D: ConstraintGraphDirecton> {
+    edges: Edges<'s, 'tcx, D>,
 }
 
-impl<'s, D: ConstraintGraphDirecton> Iterator for Successors<'s, D> {
+impl<'s, 'tcx, D: ConstraintGraphDirecton> Iterator for Successors<'s, 'tcx, D> {
     type Item = RegionVid;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -207,23 +208,26 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Successors<'s, D> {
     }
 }
 
-impl<'s, D: ConstraintGraphDirecton> graph::DirectedGraph for RegionGraph<'s, D> {
+impl<'s, 'tcx, D: ConstraintGraphDirecton> graph::DirectedGraph for RegionGraph<'s, 'tcx, D> {
     type Node = RegionVid;
 }
 
-impl<'s, D: ConstraintGraphDirecton> graph::WithNumNodes for RegionGraph<'s, D> {
+impl<'s, 'tcx, D: ConstraintGraphDirecton> graph::WithNumNodes for RegionGraph<'s, 'tcx, D> {
     fn num_nodes(&self) -> usize {
         self.constraint_graph.first_constraints.len()
     }
 }
 
-impl<'s, D: ConstraintGraphDirecton> graph::WithSuccessors for RegionGraph<'s, D> {
+impl<'s, 'tcx, D: ConstraintGraphDirecton> graph::WithSuccessors for RegionGraph<'s, 'tcx, D> {
     fn successors(&self, node: Self::Node) -> <Self as graph::GraphSuccessors<'_>>::Iter {
         self.outgoing_regions(node)
     }
 }
 
-impl<'s, 'graph, D: ConstraintGraphDirecton> graph::GraphSuccessors<'graph> for RegionGraph<'s, D> {
+impl<'s, 'graph, 'tcx, D: ConstraintGraphDirecton> graph::GraphSuccessors<'graph>
+    for RegionGraph<'s, 'tcx, D>
+{
     type Item = RegionVid;
-    type Iter = Successors<'graph, D>;
+    // FIXME - why can't this be `'graph, 'tcx`
+    type Iter = Successors<'graph, 'graph, D>;
 }
diff --git a/compiler/rustc_mir/src/borrow_check/constraints/mod.rs b/compiler/rustc_mir/src/borrow_check/constraints/mod.rs
index 3772b7d8f98..b944479ca45 100644
--- a/compiler/rustc_mir/src/borrow_check/constraints/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/constraints/mod.rs
@@ -1,7 +1,7 @@
 use rustc_data_structures::graph::scc::Sccs;
 use rustc_index::vec::IndexVec;
 use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::ty::RegionVid;
+use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
 use std::fmt;
 use std::ops::Index;
 
@@ -14,12 +14,12 @@ crate mod graph;
 /// a unique `OutlivesConstraintIndex` and you can index into the set
 /// (`constraint_set[i]`) to access the constraint details.
 #[derive(Clone, Default)]
-crate struct OutlivesConstraintSet {
-    outlives: IndexVec<OutlivesConstraintIndex, OutlivesConstraint>,
+crate struct OutlivesConstraintSet<'tcx> {
+    outlives: IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>>,
 }
 
-impl OutlivesConstraintSet {
-    crate fn push(&mut self, constraint: OutlivesConstraint) {
+impl<'tcx> OutlivesConstraintSet<'tcx> {
+    crate fn push(&mut self, constraint: OutlivesConstraint<'tcx>) {
         debug!(
             "OutlivesConstraintSet::push({:?}: {:?} @ {:?}",
             constraint.sup, constraint.sub, constraint.locations
@@ -59,21 +59,21 @@ impl OutlivesConstraintSet {
         Sccs::new(region_graph)
     }
 
-    crate fn outlives(&self) -> &IndexVec<OutlivesConstraintIndex, OutlivesConstraint> {
+    crate fn outlives(&self) -> &IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
         &self.outlives
     }
 }
 
-impl Index<OutlivesConstraintIndex> for OutlivesConstraintSet {
-    type Output = OutlivesConstraint;
+impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {
+    type Output = OutlivesConstraint<'tcx>;
 
     fn index(&self, i: OutlivesConstraintIndex) -> &Self::Output {
         &self.outlives[i]
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub struct OutlivesConstraint {
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub struct OutlivesConstraint<'tcx> {
     // NB. The ordering here is not significant for correctness, but
     // it is for convenience. Before we dump the constraints in the
     // debugging logs, we sort them, and we'd like the "super region"
@@ -89,11 +89,18 @@ pub struct OutlivesConstraint {
 
     /// What caused this constraint?
     pub category: ConstraintCategory,
+
+    /// Variance diagnostic information
+    pub variance_info: VarianceDiagInfo<'tcx>,
 }
 
-impl fmt::Debug for OutlivesConstraint {
+impl<'tcx> fmt::Debug for OutlivesConstraint<'tcx> {
     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(formatter, "({:?}: {:?}) due to {:?}", self.sup, self.sub, self.locations)
+        write!(
+            formatter,
+            "({:?}: {:?}) due to {:?} ({:?})",
+            self.sup, self.sub, self.locations, self.variance_info
+        )
     }
 }
 
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
index 1b0cae51d58..e9f1ecb9bbc 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs
@@ -15,6 +15,7 @@ use rustc_middle::ty::{self, RegionVid, TyCtxt};
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
 
+use crate::borrow_check::region_infer::BlameConstraint;
 use crate::borrow_check::{
     borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt,
     WriteKind,
@@ -289,12 +290,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         borrow_region: RegionVid,
         outlived_region: RegionVid,
     ) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
-        let (category, from_closure, span) = self.regioncx.best_blame_constraint(
-            &self.body,
-            borrow_region,
-            NllRegionVariableOrigin::FreeRegion,
-            |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
-        );
+        let BlameConstraint { category, from_closure, span, variance_info: _ } =
+            self.regioncx.best_blame_constraint(
+                &self.body,
+                borrow_region,
+                NllRegionVariableOrigin::FreeRegion,
+                |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
+            );
 
         let outlived_fr_name = self.give_region_a_name(outlived_region);
 
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
index 8665ef06126..feb7672f650 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
@@ -13,6 +13,7 @@ use rustc_span::Span;
 
 use crate::util::borrowck_errors;
 
+use crate::borrow_check::region_infer::BlameConstraint;
 use crate::borrow_check::{
     nll::ConstraintDescription,
     region_infer::{values::RegionElement, TypeTest},
@@ -275,12 +276,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     ) {
         debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
 
-        let (category, _, span) =
+        let BlameConstraint { category, span, variance_info, from_closure: _ } =
             self.regioncx.best_blame_constraint(&self.body, fr, fr_origin, |r| {
                 self.regioncx.provides_universal_region(r, fr, outlived_fr)
             });
 
-        debug!("report_region_error: category={:?} {:?}", category, span);
+        debug!("report_region_error: category={:?} {:?} {:?}", category, span, variance_info);
         // Check if we can use one of the "nice region errors".
         if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
             let nice = NiceRegionError::new_from_span(self.infcx, span, o, f);
@@ -309,7 +310,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             span,
         };
 
-        let diag = match (category, fr_is_local, outlived_fr_is_local) {
+        let mut diag = match (category, fr_is_local, outlived_fr_is_local) {
             (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => {
                 self.report_fnmut_error(&errci, kind)
             }
@@ -332,6 +333,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             }
         };
 
+        match variance_info {
+            ty::VarianceDiagInfo::None => {}
+            ty::VarianceDiagInfo::Mut { kind, ty } => {
+                let kind_name = match kind {
+                    ty::VarianceDiagMutKind::Ref => "reference",
+                    ty::VarianceDiagMutKind::RawPtr => "pointer",
+                };
+                diag.note(&format!("requirement occurs because of a mutable {kind_name} to {ty}",));
+                diag.note(&format!("mutable {kind_name}s are invariant over their type parameter"));
+                diag.help("see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance");
+            }
+        }
+
         diag.buffer(&mut self.errors_buffer);
     }
 
diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs b/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs
index 5892ef37eba..213ebff12ab 100644
--- a/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs
+++ b/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs
@@ -74,7 +74,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
         constraints.sort();
         for constraint in &constraints {
-            let OutlivesConstraint { sup, sub, locations, category } = constraint;
+            let OutlivesConstraint { sup, sub, locations, category, variance_info: _ } = constraint;
             let (name, arg) = match locations {
                 Locations::All(span) => {
                     ("All", tcx.sess.source_map().span_to_embeddable_string(*span))
diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs b/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs
index 7156612f473..b944d74e6f2 100644
--- a/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs
+++ b/compiler/rustc_mir/src/borrow_check/region_infer/graphviz.rs
@@ -35,7 +35,7 @@ struct RawConstraints<'a, 'tcx> {
 
 impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
     type Node = RegionVid;
-    type Edge = OutlivesConstraint;
+    type Edge = OutlivesConstraint<'tcx>;
 
     fn graph_id(&'this self) -> dot::Id<'this> {
         dot::Id::new("RegionInferenceContext").unwrap()
@@ -49,31 +49,31 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> {
     fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
         dot::LabelText::LabelStr(format!("{:?}", n).into())
     }
-    fn edge_label(&'this self, e: &OutlivesConstraint) -> dot::LabelText<'this> {
+    fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> {
         dot::LabelText::LabelStr(format!("{:?}", e.locations).into())
     }
 }
 
 impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> {
     type Node = RegionVid;
-    type Edge = OutlivesConstraint;
+    type Edge = OutlivesConstraint<'tcx>;
 
     fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
         let vids: Vec<RegionVid> = self.regioncx.definitions.indices().collect();
         vids.into()
     }
-    fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint> {
+    fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint<'tcx>> {
         (&self.regioncx.constraints.outlives().raw[..]).into()
     }
 
     // Render `a: b` as `a -> b`, indicating the flow
     // of data during inference.
 
-    fn source(&'this self, edge: &OutlivesConstraint) -> RegionVid {
+    fn source(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
         edge.sup
     }
 
-    fn target(&'this self, edge: &OutlivesConstraint) -> RegionVid {
+    fn target(&'this self, edge: &OutlivesConstraint<'tcx>) -> RegionVid {
         edge.sub
     }
 }
diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs
index f4d78ac04cb..dded7a7e3cf 100644
--- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs
@@ -54,7 +54,7 @@ pub struct RegionInferenceContext<'tcx> {
     liveness_constraints: LivenessValues<RegionVid>,
 
     /// The outlives constraints computed by the type-check.
-    constraints: Frozen<OutlivesConstraintSet>,
+    constraints: Frozen<OutlivesConstraintSet<'tcx>>,
 
     /// The constraint-set, but in graph form, making it easy to traverse
     /// the constraints adjacent to a particular region. Used to construct
@@ -227,10 +227,10 @@ enum RegionRelationCheckResult {
     Error,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum Trace {
+#[derive(Clone, PartialEq, Eq, Debug)]
+enum Trace<'tcx> {
     StartRegion,
-    FromOutlivesConstraint(OutlivesConstraint),
+    FromOutlivesConstraint(OutlivesConstraint<'tcx>),
     NotVisited,
 }
 
@@ -247,7 +247,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         universal_regions: Rc<UniversalRegions<'tcx>>,
         placeholder_indices: Rc<PlaceholderIndices>,
         universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
-        outlives_constraints: OutlivesConstraintSet,
+        outlives_constraints: OutlivesConstraintSet<'tcx>,
         member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
         closure_bounds_mapping: FxHashMap<
             Location,
@@ -1750,20 +1750,35 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     crate fn retrieve_closure_constraint_info(
         &self,
         body: &Body<'tcx>,
-        constraint: &OutlivesConstraint,
-    ) -> (ConstraintCategory, bool, Span) {
+        constraint: &OutlivesConstraint<'tcx>,
+    ) -> BlameConstraint<'tcx> {
         let loc = match constraint.locations {
-            Locations::All(span) => return (constraint.category, false, span),
+            Locations::All(span) => {
+                return BlameConstraint {
+                    category: constraint.category,
+                    from_closure: false,
+                    span,
+                    variance_info: constraint.variance_info.clone(),
+                };
+            }
             Locations::Single(loc) => loc,
         };
 
         let opt_span_category =
             self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub));
-        opt_span_category.map(|&(category, span)| (category, true, span)).unwrap_or((
-            constraint.category,
-            false,
-            body.source_info(loc).span,
-        ))
+        opt_span_category
+            .map(|&(category, span)| BlameConstraint {
+                category,
+                from_closure: true,
+                span: span,
+                variance_info: constraint.variance_info.clone(),
+            })
+            .unwrap_or(BlameConstraint {
+                category: constraint.category,
+                from_closure: false,
+                span: body.source_info(loc).span,
+                variance_info: constraint.variance_info.clone(),
+            })
     }
 
     /// Finds a good span to blame for the fact that `fr1` outlives `fr2`.
@@ -1774,9 +1789,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         fr1_origin: NllRegionVariableOrigin,
         fr2: RegionVid,
     ) -> (ConstraintCategory, Span) {
-        let (category, _, span) = self.best_blame_constraint(body, fr1, fr1_origin, |r| {
-            self.provides_universal_region(r, fr1, fr2)
-        });
+        let BlameConstraint { category, span, .. } =
+            self.best_blame_constraint(body, fr1, fr1_origin, |r| {
+                self.provides_universal_region(r, fr1, fr2)
+            });
         (category, span)
     }
 
@@ -1792,7 +1808,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         from_region: RegionVid,
         target_test: impl Fn(RegionVid) -> bool,
-    ) -> Option<(Vec<OutlivesConstraint>, RegionVid)> {
+    ) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
         let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
         context[from_region] = Trace::StartRegion;
 
@@ -1816,14 +1832,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 let mut result = vec![];
                 let mut p = r;
                 loop {
-                    match context[p] {
+                    match context[p].clone() {
                         Trace::NotVisited => {
                             bug!("found unvisited region {:?} on path to {:?}", p, r)
                         }
 
                         Trace::FromOutlivesConstraint(c) => {
-                            result.push(c);
                             p = c.sup;
+                            result.push(c);
                         }
 
                         Trace::StartRegion => {
@@ -1846,7 +1862,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
             // Always inline this closure because it can be hot.
             let mut handle_constraint = #[inline(always)]
-            |constraint: OutlivesConstraint| {
+            |constraint: OutlivesConstraint<'tcx>| {
                 debug_assert_eq!(constraint.sup, r);
                 let sub_region = constraint.sub;
                 if let Trace::NotVisited = context[sub_region] {
@@ -1870,6 +1886,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     sub: constraint.min_choice,
                     locations: Locations::All(p_c.definition_span),
                     category: ConstraintCategory::OpaqueType,
+                    variance_info: ty::VarianceDiagInfo::default(),
                 };
                 handle_constraint(constraint);
             }
@@ -1967,7 +1984,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         from_region: RegionVid,
         from_region_origin: NllRegionVariableOrigin,
         target_test: impl Fn(RegionVid) -> bool,
-    ) -> (ConstraintCategory, bool, Span) {
+    ) -> BlameConstraint<'tcx> {
         debug!(
             "best_blame_constraint(from_region={:?}, from_region_origin={:?})",
             from_region, from_region_origin
@@ -1979,7 +1996,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         debug!(
             "best_blame_constraint: path={:#?}",
             path.iter()
-                .map(|&c| format!(
+                .map(|c| format!(
                     "{:?} ({:?}: {:?})",
                     c,
                     self.constraint_sccs.scc(c.sup),
@@ -1989,13 +2006,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         );
 
         // Classify each of the constraints along the path.
-        let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path
+        let mut categorized_path: Vec<BlameConstraint<'tcx>> = path
             .iter()
             .map(|constraint| {
                 if constraint.category == ConstraintCategory::ClosureBounds {
                     self.retrieve_closure_constraint_info(body, &constraint)
                 } else {
-                    (constraint.category, false, constraint.locations.span(body))
+                    BlameConstraint {
+                        category: constraint.category,
+                        from_closure: false,
+                        span: constraint.locations.span(body),
+                        variance_info: constraint.variance_info.clone(),
+                    }
                 }
             })
             .collect();
@@ -2067,12 +2089,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         };
 
         let find_region = |i: &usize| {
-            let constraint = path[*i];
+            let constraint = &path[*i];
 
             let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
 
             if blame_source {
-                match categorized_path[*i].0 {
+                match categorized_path[*i].category {
                     ConstraintCategory::OpaqueType
                     | ConstraintCategory::Boring
                     | ConstraintCategory::BoringNoLocation
@@ -2083,7 +2105,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     _ => constraint_sup_scc != target_scc,
                 }
             } else {
-                match categorized_path[*i].0 {
+                match categorized_path[*i].category {
                     ConstraintCategory::OpaqueType
                     | ConstraintCategory::Boring
                     | ConstraintCategory::BoringNoLocation
@@ -2103,37 +2125,42 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         if let Some(i) = best_choice {
             if let Some(next) = categorized_path.get(i + 1) {
-                if matches!(categorized_path[i].0, ConstraintCategory::Return(_))
-                    && next.0 == ConstraintCategory::OpaqueType
+                if matches!(categorized_path[i].category, ConstraintCategory::Return(_))
+                    && next.category == ConstraintCategory::OpaqueType
                 {
                     // The return expression is being influenced by the return type being
                     // impl Trait, point at the return type and not the return expr.
-                    return *next;
+                    return next.clone();
                 }
             }
 
-            if categorized_path[i].0 == ConstraintCategory::Return(ReturnConstraint::Normal) {
+            if categorized_path[i].category == ConstraintCategory::Return(ReturnConstraint::Normal)
+            {
                 let field = categorized_path.iter().find_map(|p| {
-                    if let ConstraintCategory::ClosureUpvar(f) = p.0 { Some(f) } else { None }
+                    if let ConstraintCategory::ClosureUpvar(f) = p.category {
+                        Some(f)
+                    } else {
+                        None
+                    }
                 });
 
                 if let Some(field) = field {
-                    categorized_path[i].0 =
+                    categorized_path[i].category =
                         ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field));
                 }
             }
 
-            return categorized_path[i];
+            return categorized_path[i].clone();
         }
 
         // If that search fails, that is.. unusual. Maybe everything
         // is in the same SCC or something. In that case, find what
         // appears to be the most interesting point to report to the
         // user via an even more ad-hoc guess.
-        categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
+        categorized_path.sort_by(|p0, p1| p0.category.cmp(&p1.category));
         debug!("`: sorted_path={:#?}", categorized_path);
 
-        *categorized_path.first().unwrap()
+        categorized_path.remove(0)
     }
 }
 
@@ -2228,3 +2255,11 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
             .collect()
     }
 }
+
+#[derive(Clone, Debug)]
+pub struct BlameConstraint<'tcx> {
+    pub category: ConstraintCategory,
+    pub from_closure: bool,
+    pub span: Span,
+    pub variance_info: ty::VarianceDiagInfo<'tcx>,
+}
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs b/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs
index 8513e5e531b..eb11b937143 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/constraint_conversion.rs
@@ -143,6 +143,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
             category: self.category,
             sub,
             sup,
+            variance_info: ty::VarianceDiagInfo::default(),
         });
     }
 
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/liveness/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/mod.rs
index bddcd34ed3e..a34ae281b70 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/liveness/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/liveness/mod.rs
@@ -107,7 +107,7 @@ fn compute_live_locals(
 fn regions_that_outlive_free_regions(
     num_region_vars: usize,
     universal_regions: &UniversalRegions<'tcx>,
-    constraint_set: &OutlivesConstraintSet,
+    constraint_set: &OutlivesConstraintSet<'tcx>,
 ) -> FxHashSet<RegionVid> {
     // Build a graph of the outlives constraints thus far. This is
     // a reverse graph, so for each constraint `R1: R2` we have an
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
index d27fcb2f26f..e294f128f2e 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
@@ -226,7 +226,7 @@ fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
         let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
         let location_table = cx.location_table;
         facts.outlives.extend(cx.constraints.outlives_constraints.outlives().iter().flat_map(
-            |constraint: &OutlivesConstraint| {
+            |constraint: &OutlivesConstraint<'_>| {
                 if let Some(from_location) = constraint.locations.from_location() {
                     Either::Left(iter::once((
                         constraint.sup,
@@ -572,7 +572,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
 
         let locations = location.to_locations();
         for constraint in constraints.outlives().iter() {
-            let mut constraint = *constraint;
+            let mut constraint = constraint.clone();
             constraint.locations = locations;
             if let ConstraintCategory::Return(_)
             | ConstraintCategory::UseAsConst
@@ -862,7 +862,7 @@ crate struct MirTypeckRegionConstraints<'tcx> {
     /// hence it must report on their liveness constraints.
     crate liveness_constraints: LivenessValues<RegionVid>,
 
-    crate outlives_constraints: OutlivesConstraintSet,
+    crate outlives_constraints: OutlivesConstraintSet<'tcx>,
 
     crate member_constraints: MemberConstraintSet<'tcx, RegionVid>,
 
@@ -2535,6 +2535,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 sub: borrow_region.to_region_vid(),
                                 locations: location.to_locations(),
                                 category,
+                                variance_info: ty::VarianceDiagInfo::default(),
                             });
 
                             match mutbl {
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs b/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs
index 249945f04b7..f97252a117a 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs
@@ -94,7 +94,12 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
         )
     }
 
-    fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
+    fn push_outlives(
+        &mut self,
+        sup: ty::Region<'tcx>,
+        sub: ty::Region<'tcx>,
+        info: ty::VarianceDiagInfo<'tcx>,
+    ) {
         if let Some(borrowck_context) = &mut self.borrowck_context {
             let sub = borrowck_context.universal_regions.to_region_vid(sub);
             let sup = borrowck_context.universal_regions.to_region_vid(sup);
@@ -103,6 +108,7 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
                 sub,
                 locations: self.locations,
                 category: self.category,
+                variance_info: info,
             });
         }
     }
diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs
index 12a36976f1d..1da17bddcb7 100644
--- a/compiler/rustc_mir/src/lib.rs
+++ b/compiler/rustc_mir/src/lib.rs
@@ -15,6 +15,7 @@ Rust MIR: a lowered representation of Rust.
 #![feature(crate_visibility_modifier)]
 #![feature(decl_macro)]
 #![feature(exact_size_is_empty)]
+#![feature(format_args_capture)]
 #![feature(iter_zip)]
 #![feature(never_type)]
 #![feature(map_try_insert)]
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs
index de6336b254b..01276495c18 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_typeck/src/check/dropck.rs
@@ -310,6 +310,7 @@ impl TypeRelation<'tcx> for SimpleEqRelation<'tcx> {
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         _: ty::Variance,
+        _info: ty::VarianceDiagInfo<'tcx>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
diff --git a/src/test/ui/c-variadic/variadic-ffi-4.stderr b/src/test/ui/c-variadic/variadic-ffi-4.stderr
index dd67514d02a..cf73403bbae 100644
--- a/src/test/ui/c-variadic/variadic-ffi-4.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-4.stderr
@@ -64,6 +64,10 @@ LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut
    |                                               has type `&mut VaListImpl<'1>`
 LL |     ap0 = &mut ap1;
    |     ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |
+   = note: requirement occurs because of a mutable reference to VaListImpl<'_>
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
   --> $DIR/variadic-ffi-4.rs:28:5
@@ -74,6 +78,10 @@ LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut
    |                                               has type `&mut VaListImpl<'1>`
 LL |     ap0 = &mut ap1;
    |     ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1`
+   |
+   = note: requirement occurs because of a mutable reference to VaListImpl<'_>
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error[E0597]: `ap1` does not live long enough
   --> $DIR/variadic-ffi-4.rs:28:11
diff --git a/src/test/ui/match/match-ref-mut-invariance.nll.stderr b/src/test/ui/match/match-ref-mut-invariance.nll.stderr
index 1658efa28bf..1dc29d2088c 100644
--- a/src/test/ui/match/match-ref-mut-invariance.nll.stderr
+++ b/src/test/ui/match/match-ref-mut-invariance.nll.stderr
@@ -9,6 +9,9 @@ LL |         match self.0 { ref mut x => x }
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable reference to &i32
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr b/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr
index dc227a36566..8b87c3da28b 100644
--- a/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr
+++ b/src/test/ui/match/match-ref-mut-let-invariance.nll.stderr
@@ -10,6 +10,9 @@ LL |         x
    |         ^ returning this value requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable reference to &i32
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/type-check-pointer-coercions.stderr b/src/test/ui/nll/type-check-pointer-coercions.stderr
index 39fd98f7151..ccb3d33ac40 100644
--- a/src/test/ui/nll/type-check-pointer-coercions.stderr
+++ b/src/test/ui/nll/type-check-pointer-coercions.stderr
@@ -34,6 +34,9 @@ LL |     x
    |     ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable pointer to &i32
+   = note: mutable pointers are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
   --> $DIR/type-check-pointer-coercions.rs:13:5
@@ -47,6 +50,9 @@ LL |     x
    |     ^ returning this value requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable pointer to &i32
+   = note: mutable pointers are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 help: `'b` and `'a` must be the same: replace one with the other
 
diff --git a/src/test/ui/nll/type-check-pointer-comparisons.stderr b/src/test/ui/nll/type-check-pointer-comparisons.stderr
index f350b861eb6..b488af820b8 100644
--- a/src/test/ui/nll/type-check-pointer-comparisons.stderr
+++ b/src/test/ui/nll/type-check-pointer-comparisons.stderr
@@ -9,6 +9,9 @@ LL |     x == y;
    |     ^ requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable reference to &i32
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
   --> $DIR/type-check-pointer-comparisons.rs:6:10
@@ -21,6 +24,9 @@ LL |     x == y;
    |          ^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable reference to &i32
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 help: `'a` and `'b` must be the same: replace one with the other
 
@@ -35,6 +41,9 @@ LL |     x == y;
    |     ^ requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable pointer to &i32
+   = note: mutable pointers are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
   --> $DIR/type-check-pointer-comparisons.rs:12:10
@@ -47,6 +56,9 @@ LL |     x == y;
    |          ^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable pointer to &i32
+   = note: mutable pointers are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 help: `'a` and `'b` must be the same: replace one with the other
 
@@ -61,6 +73,9 @@ LL |     f == g;
    |     ^ requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable reference to &i32
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
   --> $DIR/type-check-pointer-comparisons.rs:18:10
@@ -73,6 +88,9 @@ LL |     f == g;
    |          ^ requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable reference to &i32
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 help: `'a` and `'b` must be the same: replace one with the other
 
diff --git a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr
index 695f5506d5e..4ddea2c27b2 100644
--- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr
+++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr
@@ -23,6 +23,9 @@ LL |     a(x, y);
    |     ^^^^^^^ argument requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable reference to &isize
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: higher-ranked subtype error
   --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:12
diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr
index a28f7aa3e08..a9cf128bb62 100644
--- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr
+++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr
@@ -23,6 +23,9 @@ LL |     a(x, y, z);
    |     ^^^^^^^^^^ argument requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable reference to &isize
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: higher-ranked subtype error
   --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr
index 37f7fcf2e33..db86572f1cf 100644
--- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr
+++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr
@@ -23,6 +23,9 @@ LL |     a(x, y);
    |     ^^^^^^^ argument requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable reference to &isize
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: higher-ranked subtype error
   --> $DIR/regions-lifetime-bounds-on-fns.rs:20:12
diff --git a/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr b/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr
index f92923a1125..bf325d56013 100644
--- a/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr
+++ b/src/test/ui/regions/regions-trait-object-subtyping.nll.stderr
@@ -10,6 +10,9 @@ LL |     x
    |     ^ returning this value requires that `'a` must outlive `'b`
    |
    = help: consider adding the following bound: `'a: 'b`
+   = note: requirement occurs because of a mutable reference to dyn Dummy
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: lifetime may not live long enough
   --> $DIR/regions-trait-object-subtyping.rs:22:5
@@ -23,6 +26,9 @@ LL |     x
    |     ^ returning this value requires that `'b` must outlive `'a`
    |
    = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a mutable reference to dyn Dummy
+   = note: mutable references are invariant over their type parameter
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
 
 error: aborting due to 2 previous errors