)
}
- 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,
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(())
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
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 }),
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_variance: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
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 }),
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
// 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)
}
}
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
// 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)
}
}
/// - 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
/// 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>);
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 {
/// 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
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
+ info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
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);
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)
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
+ _: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
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;
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
+ info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T>;
///////////////////////////////////////////////////////////////////////////
// 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 })
}
}
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)
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()
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 })
}
}
(&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))
}
}
(&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))
}
(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,
}
}
}
+
+/// 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
+ }
+}
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::{
/// 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;
}
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
}
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
}
/// 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);
/// 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() {
}
}
-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
sub: next_static_idx.into(),
locations: Locations::All(DUMMY_SP),
category: ConstraintCategory::Internal,
+ variance_info: VarianceDiagInfo::default(),
})
} else {
None
/// 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 {
/// 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> {
}
}
-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>;
}
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;
/// 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
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"
/// 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
+ )
}
}
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,
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);
use crate::util::borrowck_errors;
+use crate::borrow_check::region_infer::BlameConstraint;
use crate::borrow_check::{
nll::ConstraintDescription,
region_infer::{values::RegionElement, TypeTest},
) {
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);
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)
}
}
};
+ 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);
}
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))
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()
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
}
}
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
Error,
}
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum Trace {
+#[derive(Clone, PartialEq, Eq, Debug)]
+enum Trace<'tcx> {
StartRegion,
- FromOutlivesConstraint(OutlivesConstraint),
+ FromOutlivesConstraint(OutlivesConstraint<'tcx>),
NotVisited,
}
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,
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`.
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)
}
&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;
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 => {
// 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] {
sub: constraint.min_choice,
locations: Locations::All(p_c.definition_span),
category: ConstraintCategory::OpaqueType,
+ variance_info: ty::VarianceDiagInfo::default(),
};
handle_constraint(constraint);
}
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
debug!(
"best_blame_constraint: path={:#?}",
path.iter()
- .map(|&c| format!(
+ .map(|c| format!(
"{:?} ({:?}: {:?})",
c,
self.constraint_sccs.scc(c.sup),
);
// 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();
};
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
_ => constraint_sup_scc != target_scc,
}
} else {
- match categorized_path[*i].0 {
+ match categorized_path[*i].category {
ConstraintCategory::OpaqueType
| ConstraintCategory::Boring
| ConstraintCategory::BoringNoLocation
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)
}
}
.collect()
}
}
+
+#[derive(Clone, Debug)]
+pub struct BlameConstraint<'tcx> {
+ pub category: ConstraintCategory,
+ pub from_closure: bool,
+ pub span: Span,
+ pub variance_info: ty::VarianceDiagInfo<'tcx>,
+}
category: self.category,
sub,
sup,
+ variance_info: ty::VarianceDiagInfo::default(),
});
}
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
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,
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
/// 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>,
sub: borrow_region.to_region_vid(),
locations: location.to_locations(),
category,
+ variance_info: ty::VarianceDiagInfo::default(),
});
match mutbl {
)
}
- 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);
sub,
locations: self.locations,
category: self.category,
+ variance_info: info,
});
}
}
#![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)]
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
+ _info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
| 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
| 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
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
| ^ 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
| ^ 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
| ^ 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
| ^ 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
| ^ 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
| ^ 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
| ^ 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
| ^ 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
| ^ 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
| ^^^^^^^ 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
| ^^^^^^^^^^ 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
| ^^^^^^^ 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
| ^ 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
| ^ 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