Add variance-related information to lifetime error messages

This commit is contained in:
Aaron Hill 2021-05-13 09:30:14 -04:00
parent 86b0bafbf1
commit fad2242ff7
No known key found for this signature in database
GPG Key ID: B4087E510E98B164
33 changed files with 397 additions and 144 deletions

View File

@ -660,7 +660,12 @@ fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<
)
}
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,

View File

@ -371,9 +371,12 @@ pub fn instantiate(
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 @@ fn relate_item_substs(
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 @@ fn consts(
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 @@ fn a_is_expected(&self) -> bool {
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 @@ fn consts(
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 }),

View File

@ -59,6 +59,7 @@ fn relate_item_substs(
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {

View File

@ -43,6 +43,7 @@ fn a_is_expected(&self) -> bool {
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 @@ fn binders<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)
}
}

View File

@ -43,6 +43,7 @@ fn a_is_expected(&self) -> bool {
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 @@ fn binders<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)
}
}

View File

@ -55,6 +55,8 @@ pub struct TypeRelating<'me, 'tcx, D>
/// - 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 @@ pub fn new(
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 @@ fn replace_bound_region(
/// 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 @@ fn a_is_expected(&self) -> bool {
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 @@ fn relate_with_variance<T: Relate<'tcx>>(
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 @@ fn regions(
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 @@ fn a_is_expected(&self) -> bool {
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {

View File

@ -62,6 +62,7 @@ fn with_cause<F, R>(&mut self, cause: Cause, f: F) -> R
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {

View File

@ -46,6 +46,7 @@ fn a_is_expected(&self) -> bool {
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
_: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {

View File

@ -71,7 +71,7 @@
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;

View File

@ -67,6 +67,7 @@ fn relate_item_substs(
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 @@ fn relate<R: TypeRelation<'tcx>>(
///////////////////////////////////////////////////////////////////////////
// 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 @@ fn relate<R: TypeRelation<'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 @@ fn relate<R: TypeRelation<'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,

View File

@ -2181,3 +2181,55 @@ pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool {
}
}
}
/// 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
}
}

View File

@ -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 @@
/// 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 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 @@ fn is_normal() -> bool {
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 @@ fn next(&mut self) -> Option<Self::Item> {
sub: next_static_idx.into(),
locations: Locations::All(DUMMY_SP),
category: ConstraintCategory::Internal,
variance_info: VarianceDiagInfo::default(),
})
} else {
None
@ -167,19 +168,19 @@ fn next(&mut self) -> Option<Self::Item> {
/// 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 @@ 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>;
}

View File

@ -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 @@
/// 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
)
}
}

View File

@ -15,6 +15,7 @@
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 @@ fn free_region_constraint_info(
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);

View File

@ -13,6 +13,7 @@
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 @@ pub(in crate::borrow_check) fn report_region_error(
) {
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 @@ pub(in crate::borrow_check) fn report_region_error(
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 @@ pub(in crate::borrow_check) fn report_region_error(
}
};
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);
}

View File

@ -74,7 +74,7 @@ fn for_each_constraint(
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))

View File

@ -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 @@ fn node_shape(&'this self, _node: &RegionVid) -> Option<dot::LabelText<'this>> {
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
}
}

View File

@ -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 @@ pub(in crate::borrow_check) fn new(
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 @@ fn check_member_constraints(
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 @@ fn check_member_constraints(
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 @@ fn check_member_constraints(
&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 @@ fn check_member_constraints(
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 @@ fn check_member_constraints(
// 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 @@ fn check_member_constraints(
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 @@ fn check_member_constraints(
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 @@ fn check_member_constraints(
debug!(
"best_blame_constraint: path={:#?}",
path.iter()
.map(|&c| format!(
.map(|c| format!(
"{:?} ({:?}: {:?})",
c,
self.constraint_sccs.scc(c.sup),
@ -1989,13 +2006,18 @@ fn check_member_constraints(
);
// 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 @@ fn check_member_constraints(
};
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 @@ fn check_member_constraints(
_ => 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 @@ fn check_member_constraints(
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 @@ fn apply_requirements(
.collect()
}
}
#[derive(Clone, Debug)]
pub struct BlameConstraint<'tcx> {
pub category: ConstraintCategory,
pub from_closure: bool,
pub span: Span,
pub variance_info: ty::VarianceDiagInfo<'tcx>,
}

View File

@ -143,6 +143,7 @@ fn add_outlives(&mut self, sup: ty::RegionVid, sub: ty::RegionVid) {
category: self.category,
sub,
sup,
variance_info: ty::VarianceDiagInfo::default(),
});
}

View File

@ -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

View File

@ -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 @@ fn sanitize_promoted(&mut self, promoted_body: &'b Body<'tcx>, location: Locatio
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 @@ struct BorrowCheckContext<'a, '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 @@ fn add_reborrow_constraint(
sub: borrow_region.to_region_vid(),
locations: location.to_locations(),
category,
variance_info: ty::VarianceDiagInfo::default(),
});
match mutbl {

View File

@ -94,7 +94,12 @@ fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<
)
}
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 @@ fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
sub,
locations: self.locations,
category: self.category,
variance_info: info,
});
}
}

View File

@ -15,6 +15,7 @@
#![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)]

View File

@ -310,6 +310,7 @@ fn a_is_expected(&self) -> bool {
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
_info: ty::VarianceDiagInfo<'tcx>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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