Skip to content

Commit 35fff69

Browse files
committed
Auto merge of #85343 - Aaron1011:variance-diag, r=estebank
Add variance-related information to lifetime error messages This PR adds a basic framework for displaying variance-related information in error messages. For example: ``` error: lifetime may not live long enough --> $DIR/type-check-pointer-comparisons.rs:12:5 | LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | x == y; | ^ requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a mutable pointer to &i32 = note: mutable pointers are invariant over their type parameter = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance ``` The last three lines are new. This is accomplished by adding a new struct `VarianceDiagInfo`, and passing it along through the various relation methods. When relating types that change the variance (e.g. `&mut T` or `*mut T`), we pass a more specific `VarianceDiagInfo` storing information about the cause of the variance change. When an error, we use the `VarianceDiagInfo` to add additional information to the error message. This PR doesn't change any variance-related computation or behavior - only diagnostic messages. Therefore, the implementation is quite incomplete - more detailed error messages can be filled in in subsequent PRs. Limitations: * We only attempt to deal with invariance - since it's at the bottom of the 'variance lattice', our variance will never change again after it becomes invariant. Handling contravariance would be trickier, since we can change between contravariance and covariance multiple times (e.g. `fn(fn(&'static u8))`). Since contravariance (AFAIK) is only used for function arguments, we can probably get away without a very fancy message for cases involving contravariance. * `VarianceDiagInfo` currently only handles mutable pointers/references. However, user-defined types (structs, enums, and unions) have the variance of their type parameters inferred, so it would be good to eventually display information about that. We'll want to try to find a balance between displaying too much and too little information about how the variance was inferred. * The improved error messages are only displayed when `#![feature(nll)]` / `-Z borrowck=mir` is enabled. If issue #58781 is not resolved relatively soon, then we might want to duplicate some of this logic in the 'current' (non-NLL) region/outlives handling code.
2 parents 5b638c1 + fad2242 commit 35fff69

33 files changed

+397
-144
lines changed

compiler/rustc_infer/src/infer/canonical/query_response.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,12 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
660660
)
661661
}
662662

663-
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
663+
fn push_outlives(
664+
&mut self,
665+
sup: ty::Region<'tcx>,
666+
sub: ty::Region<'tcx>,
667+
_info: ty::VarianceDiagInfo<'tcx>,
668+
) {
664669
self.obligations.push(Obligation {
665670
cause: self.cause.clone(),
666671
param_env: self.param_env,

compiler/rustc_infer/src/infer/combine.rs

+20-5
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
371371
match dir {
372372
EqTo => self.equate(a_is_expected).relate(a_ty, b_ty),
373373
SubtypeOf => self.sub(a_is_expected).relate(a_ty, b_ty),
374-
SupertypeOf => {
375-
self.sub(a_is_expected).relate_with_variance(ty::Contravariant, a_ty, b_ty)
376-
}
374+
SupertypeOf => self.sub(a_is_expected).relate_with_variance(
375+
ty::Contravariant,
376+
ty::VarianceDiagInfo::default(),
377+
a_ty,
378+
b_ty,
379+
),
377380
}?;
378381

379382
Ok(())
@@ -574,6 +577,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
574577
fn relate_with_variance<T: Relate<'tcx>>(
575578
&mut self,
576579
variance: ty::Variance,
580+
_info: ty::VarianceDiagInfo<'tcx>,
577581
a: T,
578582
b: T,
579583
) -> RelateResult<'tcx, T> {
@@ -737,7 +741,12 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
737741
if self.tcx().lazy_normalization() =>
738742
{
739743
assert_eq!(promoted, None);
740-
let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?;
744+
let substs = self.relate_with_variance(
745+
ty::Variance::Invariant,
746+
ty::VarianceDiagInfo::default(),
747+
substs,
748+
substs,
749+
)?;
741750
Ok(self.tcx().mk_const(ty::Const {
742751
ty: c.ty,
743752
val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
@@ -831,6 +840,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
831840
fn relate_with_variance<T: Relate<'tcx>>(
832841
&mut self,
833842
_variance: ty::Variance,
843+
_info: ty::VarianceDiagInfo<'tcx>,
834844
a: T,
835845
b: T,
836846
) -> RelateResult<'tcx, T> {
@@ -965,7 +975,12 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
965975
if self.tcx().lazy_normalization() =>
966976
{
967977
assert_eq!(promoted, None);
968-
let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?;
978+
let substs = self.relate_with_variance(
979+
ty::Variance::Invariant,
980+
ty::VarianceDiagInfo::default(),
981+
substs,
982+
substs,
983+
)?;
969984
Ok(self.tcx().mk_const(ty::Const {
970985
ty: c.ty,
971986
val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),

compiler/rustc_infer/src/infer/equate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
5959
fn relate_with_variance<T: Relate<'tcx>>(
6060
&mut self,
6161
_: ty::Variance,
62+
_info: ty::VarianceDiagInfo<'tcx>,
6263
a: T,
6364
b: T,
6465
) -> RelateResult<'tcx, T> {

compiler/rustc_infer/src/infer/glb.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
4343
fn relate_with_variance<T: Relate<'tcx>>(
4444
&mut self,
4545
variance: ty::Variance,
46+
_info: ty::VarianceDiagInfo<'tcx>,
4647
a: T,
4748
b: T,
4849
) -> RelateResult<'tcx, T> {
@@ -96,7 +97,7 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
9697
// When higher-ranked types are involved, computing the LUB is
9798
// very challenging, switch to invariance. This is obviously
9899
// overly conservative but works ok in practice.
99-
self.relate_with_variance(ty::Variance::Invariant, a, b)?;
100+
self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
100101
Ok(a)
101102
}
102103
}

compiler/rustc_infer/src/infer/lub.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
4343
fn relate_with_variance<T: Relate<'tcx>>(
4444
&mut self,
4545
variance: ty::Variance,
46+
_info: ty::VarianceDiagInfo<'tcx>,
4647
a: T,
4748
b: T,
4849
) -> RelateResult<'tcx, T> {
@@ -96,7 +97,7 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
9697
// When higher-ranked types are involved, computing the LUB is
9798
// very challenging, switch to invariance. This is obviously
9899
// overly conservative but works ok in practice.
99-
self.relate_with_variance(ty::Variance::Invariant, a, b)?;
100+
self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
100101
Ok(a)
101102
}
102103
}

compiler/rustc_infer/src/infer/nll_relate/mod.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ where
5555
/// - Bivariant means that it doesn't matter.
5656
ambient_variance: ty::Variance,
5757

58+
ambient_variance_info: ty::VarianceDiagInfo<'tcx>,
59+
5860
/// When we pass through a set of binders (e.g., when looking into
5961
/// a `fn` type), we push a new bound region scope onto here. This
6062
/// will contain the instantiated region for each region in those
@@ -78,7 +80,12 @@ pub trait TypeRelatingDelegate<'tcx> {
7880
/// satisfied for the two types to be related. `sub` and `sup` may
7981
/// be regions from the type or new variables created through the
8082
/// delegate.
81-
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
83+
fn push_outlives(
84+
&mut self,
85+
sup: ty::Region<'tcx>,
86+
sub: ty::Region<'tcx>,
87+
info: ty::VarianceDiagInfo<'tcx>,
88+
);
8289

8390
fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
8491

@@ -138,7 +145,14 @@ where
138145
delegate: D,
139146
ambient_variance: ty::Variance,
140147
) -> Self {
141-
Self { infcx, delegate, ambient_variance, a_scopes: vec![], b_scopes: vec![] }
148+
Self {
149+
infcx,
150+
delegate,
151+
ambient_variance,
152+
ambient_variance_info: ty::VarianceDiagInfo::default(),
153+
a_scopes: vec![],
154+
b_scopes: vec![],
155+
}
142156
}
143157

144158
fn ambient_covariance(&self) -> bool {
@@ -239,10 +253,15 @@ where
239253

240254
/// Push a new outlives requirement into our output set of
241255
/// constraints.
242-
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
256+
fn push_outlives(
257+
&mut self,
258+
sup: ty::Region<'tcx>,
259+
sub: ty::Region<'tcx>,
260+
info: ty::VarianceDiagInfo<'tcx>,
261+
) {
243262
debug!("push_outlives({:?}: {:?})", sup, sub);
244263

245-
self.delegate.push_outlives(sup, sub);
264+
self.delegate.push_outlives(sup, sub, info);
246265
}
247266

248267
/// Relate a projection type and some value type lazily. This will always
@@ -490,13 +509,15 @@ where
490509
fn relate_with_variance<T: Relate<'tcx>>(
491510
&mut self,
492511
variance: ty::Variance,
512+
info: ty::VarianceDiagInfo<'tcx>,
493513
a: T,
494514
b: T,
495515
) -> RelateResult<'tcx, T> {
496516
debug!("relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b);
497517

498518
let old_ambient_variance = self.ambient_variance;
499519
self.ambient_variance = self.ambient_variance.xform(variance);
520+
self.ambient_variance_info = self.ambient_variance_info.clone().xform(info);
500521

501522
debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance);
502523

@@ -574,12 +595,12 @@ where
574595

575596
if self.ambient_covariance() {
576597
// Covariance: a <= b. Hence, `b: a`.
577-
self.push_outlives(v_b, v_a);
598+
self.push_outlives(v_b, v_a, self.ambient_variance_info.clone());
578599
}
579600

580601
if self.ambient_contravariance() {
581602
// Contravariant: b <= a. Hence, `a: b`.
582-
self.push_outlives(v_a, v_b);
603+
self.push_outlives(v_a, v_b, self.ambient_variance_info.clone());
583604
}
584605

585606
Ok(a)
@@ -835,6 +856,7 @@ where
835856
fn relate_with_variance<T: Relate<'tcx>>(
836857
&mut self,
837858
variance: ty::Variance,
859+
_info: ty::VarianceDiagInfo<'tcx>,
838860
a: T,
839861
b: T,
840862
) -> RelateResult<'tcx, T> {

compiler/rustc_infer/src/infer/sub.rs

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
6262
fn relate_with_variance<T: Relate<'tcx>>(
6363
&mut self,
6464
variance: ty::Variance,
65+
_info: ty::VarianceDiagInfo<'tcx>,
6566
a: T,
6667
b: T,
6768
) -> RelateResult<'tcx, T> {

compiler/rustc_middle/src/ty/_match.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ impl TypeRelation<'tcx> for Match<'tcx> {
4646
fn relate_with_variance<T: Relate<'tcx>>(
4747
&mut self,
4848
_: ty::Variance,
49+
_: ty::VarianceDiagInfo<'tcx>,
4950
a: T,
5051
b: T,
5152
) -> RelateResult<'tcx, T> {

compiler/rustc_middle/src/ty/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub use self::sty::{
7171
ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig,
7272
GeneratorSubsts, GeneratorSubstsParts, ParamConst, ParamTy, PolyExistentialProjection,
7373
PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, ProjectionTy, Region, RegionKind,
74-
RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts,
74+
RegionVid, TraitRef, TyKind, TypeAndMut, UpvarSubsts, VarianceDiagInfo, VarianceDiagMutKind,
7575
};
7676
pub use self::trait_def::TraitDef;
7777

compiler/rustc_middle/src/ty/relate.rs

+57-28
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub trait TypeRelation<'tcx>: Sized {
6767
fn relate_with_variance<T: Relate<'tcx>>(
6868
&mut self,
6969
variance: ty::Variance,
70+
info: ty::VarianceDiagInfo<'tcx>,
7071
a: T,
7172
b: T,
7273
) -> RelateResult<'tcx, T>;
@@ -111,24 +112,23 @@ pub trait Relate<'tcx>: TypeFoldable<'tcx> + Copy {
111112
///////////////////////////////////////////////////////////////////////////
112113
// Relate impls
113114

114-
impl<'tcx> Relate<'tcx> for ty::TypeAndMut<'tcx> {
115-
fn relate<R: TypeRelation<'tcx>>(
116-
relation: &mut R,
117-
a: ty::TypeAndMut<'tcx>,
118-
b: ty::TypeAndMut<'tcx>,
119-
) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> {
120-
debug!("{}.mts({:?}, {:?})", relation.tag(), a, b);
121-
if a.mutbl != b.mutbl {
122-
Err(TypeError::Mutability)
123-
} else {
124-
let mutbl = a.mutbl;
125-
let variance = match mutbl {
126-
ast::Mutability::Not => ty::Covariant,
127-
ast::Mutability::Mut => ty::Invariant,
128-
};
129-
let ty = relation.relate_with_variance(variance, a.ty, b.ty)?;
130-
Ok(ty::TypeAndMut { ty, mutbl })
131-
}
115+
fn relate_type_and_mut<'tcx, R: TypeRelation<'tcx>>(
116+
relation: &mut R,
117+
a: ty::TypeAndMut<'tcx>,
118+
b: ty::TypeAndMut<'tcx>,
119+
kind: ty::VarianceDiagMutKind,
120+
) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> {
121+
debug!("{}.mts({:?}, {:?})", relation.tag(), a, b);
122+
if a.mutbl != b.mutbl {
123+
Err(TypeError::Mutability)
124+
} else {
125+
let mutbl = a.mutbl;
126+
let (variance, info) = match mutbl {
127+
ast::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None),
128+
ast::Mutability::Mut => (ty::Invariant, ty::VarianceDiagInfo::Mut { kind, ty: a.ty }),
129+
};
130+
let ty = relation.relate_with_variance(variance, info, a.ty, b.ty)?;
131+
Ok(ty::TypeAndMut { ty, mutbl })
132132
}
133133
}
134134

@@ -142,7 +142,7 @@ pub fn relate_substs<R: TypeRelation<'tcx>>(
142142

143143
let params = iter::zip(a_subst, b_subst).enumerate().map(|(i, (a, b))| {
144144
let variance = variances.map_or(ty::Invariant, |v| v[i]);
145-
relation.relate_with_variance(variance, a, b)
145+
relation.relate_with_variance(variance, ty::VarianceDiagInfo::default(), a, b)
146146
});
147147

148148
tcx.mk_substs(params)
@@ -177,7 +177,12 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> {
177177
if is_output {
178178
relation.relate(a, b)
179179
} else {
180-
relation.relate_with_variance(ty::Contravariant, a, b)
180+
relation.relate_with_variance(
181+
ty::Contravariant,
182+
ty::VarianceDiagInfo::default(),
183+
a,
184+
b,
185+
)
181186
}
182187
})
183188
.enumerate()
@@ -251,8 +256,18 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> {
251256
b.item_def_id,
252257
)))
253258
} else {
254-
let ty = relation.relate_with_variance(ty::Invariant, a.ty, b.ty)?;
255-
let substs = relation.relate_with_variance(ty::Invariant, a.substs, b.substs)?;
259+
let ty = relation.relate_with_variance(
260+
ty::Invariant,
261+
ty::VarianceDiagInfo::default(),
262+
a.ty,
263+
b.ty,
264+
)?;
265+
let substs = relation.relate_with_variance(
266+
ty::Invariant,
267+
ty::VarianceDiagInfo::default(),
268+
a.substs,
269+
b.substs,
270+
)?;
256271
Ok(ty::ExistentialProjection { item_def_id: a.item_def_id, substs, ty })
257272
}
258273
}
@@ -364,7 +379,12 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
364379

365380
(&ty::Dynamic(a_obj, a_region), &ty::Dynamic(b_obj, b_region)) => {
366381
let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| {
367-
relation.relate_with_variance(ty::Contravariant, a_region, b_region)
382+
relation.relate_with_variance(
383+
ty::Contravariant,
384+
ty::VarianceDiagInfo::default(),
385+
a_region,
386+
b_region,
387+
)
368388
})?;
369389
Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound))
370390
}
@@ -398,15 +418,20 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
398418
}
399419

400420
(&ty::RawPtr(a_mt), &ty::RawPtr(b_mt)) => {
401-
let mt = relation.relate(a_mt, b_mt)?;
421+
let mt = relate_type_and_mut(relation, a_mt, b_mt, ty::VarianceDiagMutKind::RawPtr)?;
402422
Ok(tcx.mk_ptr(mt))
403423
}
404424

405425
(&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => {
406-
let r = relation.relate_with_variance(ty::Contravariant, a_r, b_r)?;
426+
let r = relation.relate_with_variance(
427+
ty::Contravariant,
428+
ty::VarianceDiagInfo::default(),
429+
a_r,
430+
b_r,
431+
)?;
407432
let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl };
408433
let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl };
409-
let mt = relation.relate(a_mt, b_mt)?;
434+
let mt = relate_type_and_mut(relation, a_mt, b_mt, ty::VarianceDiagMutKind::Ref)?;
410435
Ok(tcx.mk_ref(r, mt))
411436
}
412437

@@ -536,8 +561,12 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
536561
(ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
537562
if au.def == bu.def && au.promoted == bu.promoted =>
538563
{
539-
let substs =
540-
relation.relate_with_variance(ty::Variance::Invariant, au.substs, bu.substs)?;
564+
let substs = relation.relate_with_variance(
565+
ty::Variance::Invariant,
566+
ty::VarianceDiagInfo::default(),
567+
au.substs,
568+
bu.substs,
569+
)?;
541570
return Ok(tcx.mk_const(ty::Const {
542571
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
543572
def: au.def,

0 commit comments

Comments
 (0)