1
Fork 0

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

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

View file

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

View file

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

View file

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