Skip to content

Commit e7eaed2

Browse files
committed
Auto merge of #107969 - b-naber:proj-relate-variance, r=lcnr
Use covariance on type relations of field projection types if possible It's fine to use covariance here unless we're in a mutating context. Fixes #96514 Supersedes #105958 r? `@lcnr`
2 parents 7e253a7 + 758cc95 commit e7eaed2

8 files changed

+159
-4
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

+25-4
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
534534
return PlaceTy::from_ty(self.tcx().ty_error());
535535
}
536536
}
537-
place_ty = self.sanitize_projection(place_ty, elem, place, location);
537+
place_ty = self.sanitize_projection(place_ty, elem, place, location, context);
538538
}
539539

540540
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
@@ -630,12 +630,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
630630
}
631631
}
632632

633+
#[instrument(skip(self), level = "debug")]
633634
fn sanitize_projection(
634635
&mut self,
635636
base: PlaceTy<'tcx>,
636637
pi: PlaceElem<'tcx>,
637638
place: &Place<'tcx>,
638639
location: Location,
640+
context: PlaceContext,
639641
) -> PlaceTy<'tcx> {
640642
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
641643
let tcx = self.tcx();
@@ -713,8 +715,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
713715
match self.field_ty(place, base, field, location) {
714716
Ok(ty) => {
715717
let ty = self.cx.normalize(ty, location);
716-
if let Err(terr) = self.cx.eq_types(
718+
debug!(?fty, ?ty);
719+
720+
if let Err(terr) = self.cx.relate_types(
717721
ty,
722+
self.get_ambient_variance(context),
718723
fty,
719724
location.to_locations(),
720725
ConstraintCategory::Boring,
@@ -743,9 +748,10 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
743748
let ty = self.sanitize_type(place, ty);
744749
let ty = self.cx.normalize(ty, location);
745750
self.cx
746-
.eq_types(
747-
base.ty,
751+
.relate_types(
748752
ty,
753+
self.get_ambient_variance(context),
754+
base.ty,
749755
location.to_locations(),
750756
ConstraintCategory::TypeAnnotation,
751757
)
@@ -760,6 +766,21 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
760766
self.tcx().ty_error()
761767
}
762768

769+
fn get_ambient_variance(&self, context: PlaceContext) -> ty::Variance {
770+
use rustc_middle::mir::visit::NonMutatingUseContext::*;
771+
use rustc_middle::mir::visit::NonUseContext::*;
772+
773+
match context {
774+
PlaceContext::MutatingUse(_) => ty::Invariant,
775+
PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
776+
PlaceContext::NonMutatingUse(
777+
Inspect | Copy | Move | SharedBorrow | ShallowBorrow | UniqueBorrow | AddressOf
778+
| Projection,
779+
) => ty::Covariant,
780+
PlaceContext::NonUse(AscribeUserTy) => ty::Covariant,
781+
}
782+
}
783+
763784
fn field_ty(
764785
&mut self,
765786
parent: &dyn fmt::Debug,
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// build-pass
2+
struct Inv<'a>(&'a mut &'a ());
3+
enum Foo<T> {
4+
Bar,
5+
Var(T),
6+
}
7+
type Supertype = Foo<for<'a> fn(Inv<'a>, Inv<'a>)>;
8+
9+
fn foo(x: Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>) {
10+
match x {
11+
Supertype::Bar => {}
12+
Supertype::Var(x) => {}
13+
}
14+
}
15+
16+
fn foo_nested(x: Foo<Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>>) {
17+
match x {
18+
Foo::Bar => {}
19+
Foo::Var(Supertype::Bar) => {}
20+
Foo::Var(Supertype::Var(x)) => {}
21+
}
22+
}
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use std::sync::Mutex;
2+
3+
static GLOBAL: Mutex<&'static str> = Mutex::new("global str");
4+
5+
struct Foo<T>(T); // `T` is covariant.
6+
7+
fn foo() {
8+
let mut x: Foo<for<'a> fn(&'a str)> = Foo(|_| ());
9+
let Foo(ref mut y): Foo<fn(&'static str)> = x;
10+
//~^ ERROR mismatched types
11+
*y = |s| *GLOBAL.lock().unwrap() = s;
12+
let string = String::from("i am shortlived");
13+
(x.0)(&string);
14+
}
15+
16+
fn main() {
17+
foo();
18+
println!("{}", GLOBAL.lock().unwrap());
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/field-projection-mutating-context.rs:9:13
3+
|
4+
LL | let Foo(ref mut y): Foo<fn(&'static str)> = x;
5+
| ^^^^^^^^^ one type is more general than the other
6+
|
7+
= note: expected fn pointer `for<'a> fn(&'a str)`
8+
found fn pointer `fn(&str)`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use std::sync::Mutex;
2+
3+
static GLOBAL: Mutex<&'static str> = Mutex::new("global str");
4+
5+
struct Foo<T>(T); // `T` is covariant.
6+
7+
fn foo<'a>(mut x: Foo<fn(&'a str)>, string: &'a str) {
8+
let Foo(ref mut y): Foo<fn(&'static str)> = x;
9+
//~^ ERROR lifetime may not live long enough
10+
*y = |s| *GLOBAL.lock().unwrap() = s;
11+
(x.0)(&string);
12+
}
13+
14+
fn main() {
15+
foo(Foo(|_| ()), &String::from("i am shortlived"));
16+
println!("{}", GLOBAL.lock().unwrap());
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/field-projection-mutating-context2.rs:8:25
3+
|
4+
LL | fn foo<'a>(mut x: Foo<fn(&'a str)>, string: &'a str) {
5+
| -- lifetime `'a` defined here
6+
LL | let Foo(ref mut y): Foo<fn(&'static str)> = x;
7+
| ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
8+
9+
error: aborting due to previous error
10+
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// build-pass
2+
3+
enum Foo<T> {
4+
Var(T),
5+
} // `T` is covariant.
6+
7+
fn foo<'b>(x: Foo<for<'a> fn(&'a ())>) {
8+
let Foo::Var(x): Foo<fn(&'b ())> = x;
9+
}
10+
11+
fn foo_nested<'b>(x: Foo<Foo<for<'a> fn(&'a ())>>) {
12+
let Foo::Var(Foo::Var(x)): Foo<Foo<fn(&'b ())>> = x;
13+
}
14+
15+
fn main() {}

tests/ui/mir/field-ty-ascription.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// build-pass
2+
3+
struct Foo<T>(T); // `T` is covariant.
4+
5+
struct Bar<T> {
6+
x: T,
7+
} // `T` is covariant.
8+
9+
fn bar<'b>(x: Bar<for<'a> fn(&'a ())>) {
10+
let Bar { x }: Bar<fn(&'b ())> = x;
11+
}
12+
13+
fn bar_nested<'b>(x: Bar<Bar<for<'a> fn(&'a ())>>) {
14+
let Bar { x: Bar { x } }: Bar<Bar<fn(&'b ())>> = x;
15+
}
16+
17+
fn bar_foo_nested<'b>(x: Bar<Foo<for<'a> fn(&'a ())>>) {
18+
let Bar { x: Foo ( x ) }: Bar<Foo<fn(&'b ())>> = x;
19+
}
20+
21+
fn foo<'b>(x: Foo<for<'a> fn(&'a ())>) {
22+
let Foo(y): Foo<fn(&'b ())> = x;
23+
}
24+
25+
fn foo_nested<'b>(x: Foo<Foo<for<'a> fn(&'a ())>>) {
26+
let Foo(Foo(y)): Foo<Foo<fn(&'b ())>> = x;
27+
}
28+
29+
fn tuple<'b>(x: (u32, for<'a> fn(&'a ()))) {
30+
let (_, y): (u32, fn(&'b ())) = x;
31+
}
32+
33+
fn tuple_nested<'b>(x: (u32, (u32, for<'a> fn(&'a ())))) {
34+
let (_, (_, y)): (u32, (u32, fn(&'b ()))) = x;
35+
}
36+
37+
fn main() {}

0 commit comments

Comments
 (0)