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 https://github.com/rust-lang/rust/issues/96514 Supersedes https://github.com/rust-lang/rust/pull/105958 r? `@lcnr`
This commit is contained in:
commit
e7eaed21d5
8 changed files with 159 additions and 4 deletions
|
@ -534,7 +534,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||||
return PlaceTy::from_ty(self.tcx().ty_error());
|
return PlaceTy::from_ty(self.tcx().ty_error());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
place_ty = self.sanitize_projection(place_ty, elem, place, location);
|
place_ty = self.sanitize_projection(place_ty, elem, place, location, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
|
if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context {
|
||||||
|
@ -630,12 +630,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn sanitize_projection(
|
fn sanitize_projection(
|
||||||
&mut self,
|
&mut self,
|
||||||
base: PlaceTy<'tcx>,
|
base: PlaceTy<'tcx>,
|
||||||
pi: PlaceElem<'tcx>,
|
pi: PlaceElem<'tcx>,
|
||||||
place: &Place<'tcx>,
|
place: &Place<'tcx>,
|
||||||
location: Location,
|
location: Location,
|
||||||
|
context: PlaceContext,
|
||||||
) -> PlaceTy<'tcx> {
|
) -> PlaceTy<'tcx> {
|
||||||
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
|
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
|
@ -713,8 +715,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||||
match self.field_ty(place, base, field, location) {
|
match self.field_ty(place, base, field, location) {
|
||||||
Ok(ty) => {
|
Ok(ty) => {
|
||||||
let ty = self.cx.normalize(ty, location);
|
let ty = self.cx.normalize(ty, location);
|
||||||
if let Err(terr) = self.cx.eq_types(
|
debug!(?fty, ?ty);
|
||||||
|
|
||||||
|
if let Err(terr) = self.cx.relate_types(
|
||||||
ty,
|
ty,
|
||||||
|
self.get_ambient_variance(context),
|
||||||
fty,
|
fty,
|
||||||
location.to_locations(),
|
location.to_locations(),
|
||||||
ConstraintCategory::Boring,
|
ConstraintCategory::Boring,
|
||||||
|
@ -743,9 +748,10 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||||
let ty = self.sanitize_type(place, ty);
|
let ty = self.sanitize_type(place, ty);
|
||||||
let ty = self.cx.normalize(ty, location);
|
let ty = self.cx.normalize(ty, location);
|
||||||
self.cx
|
self.cx
|
||||||
.eq_types(
|
.relate_types(
|
||||||
base.ty,
|
|
||||||
ty,
|
ty,
|
||||||
|
self.get_ambient_variance(context),
|
||||||
|
base.ty,
|
||||||
location.to_locations(),
|
location.to_locations(),
|
||||||
ConstraintCategory::TypeAnnotation,
|
ConstraintCategory::TypeAnnotation,
|
||||||
)
|
)
|
||||||
|
@ -760,6 +766,21 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||||
self.tcx().ty_error()
|
self.tcx().ty_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_ambient_variance(&self, context: PlaceContext) -> ty::Variance {
|
||||||
|
use rustc_middle::mir::visit::NonMutatingUseContext::*;
|
||||||
|
use rustc_middle::mir::visit::NonUseContext::*;
|
||||||
|
|
||||||
|
match context {
|
||||||
|
PlaceContext::MutatingUse(_) => ty::Invariant,
|
||||||
|
PlaceContext::NonUse(StorageDead | StorageLive | VarDebugInfo) => ty::Invariant,
|
||||||
|
PlaceContext::NonMutatingUse(
|
||||||
|
Inspect | Copy | Move | SharedBorrow | ShallowBorrow | UniqueBorrow | AddressOf
|
||||||
|
| Projection,
|
||||||
|
) => ty::Covariant,
|
||||||
|
PlaceContext::NonUse(AscribeUserTy) => ty::Covariant,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn field_ty(
|
fn field_ty(
|
||||||
&mut self,
|
&mut self,
|
||||||
parent: &dyn fmt::Debug,
|
parent: &dyn fmt::Debug,
|
||||||
|
|
24
tests/ui/mir/field-projection-invariant.rs
Normal file
24
tests/ui/mir/field-projection-invariant.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// build-pass
|
||||||
|
struct Inv<'a>(&'a mut &'a ());
|
||||||
|
enum Foo<T> {
|
||||||
|
Bar,
|
||||||
|
Var(T),
|
||||||
|
}
|
||||||
|
type Supertype = Foo<for<'a> fn(Inv<'a>, Inv<'a>)>;
|
||||||
|
|
||||||
|
fn foo(x: Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>) {
|
||||||
|
match x {
|
||||||
|
Supertype::Bar => {}
|
||||||
|
Supertype::Var(x) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo_nested(x: Foo<Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>>) {
|
||||||
|
match x {
|
||||||
|
Foo::Bar => {}
|
||||||
|
Foo::Var(Supertype::Bar) => {}
|
||||||
|
Foo::Var(Supertype::Var(x)) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
19
tests/ui/mir/field-projection-mutating-context.rs
Normal file
19
tests/ui/mir/field-projection-mutating-context.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
static GLOBAL: Mutex<&'static str> = Mutex::new("global str");
|
||||||
|
|
||||||
|
struct Foo<T>(T); // `T` is covariant.
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
let mut x: Foo<for<'a> fn(&'a str)> = Foo(|_| ());
|
||||||
|
let Foo(ref mut y): Foo<fn(&'static str)> = x;
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
*y = |s| *GLOBAL.lock().unwrap() = s;
|
||||||
|
let string = String::from("i am shortlived");
|
||||||
|
(x.0)(&string);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo();
|
||||||
|
println!("{}", GLOBAL.lock().unwrap());
|
||||||
|
}
|
12
tests/ui/mir/field-projection-mutating-context.stderr
Normal file
12
tests/ui/mir/field-projection-mutating-context.stderr
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/field-projection-mutating-context.rs:9:13
|
||||||
|
|
|
||||||
|
LL | let Foo(ref mut y): Foo<fn(&'static str)> = x;
|
||||||
|
| ^^^^^^^^^ one type is more general than the other
|
||||||
|
|
|
||||||
|
= note: expected fn pointer `for<'a> fn(&'a str)`
|
||||||
|
found fn pointer `fn(&str)`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
17
tests/ui/mir/field-projection-mutating-context2.rs
Normal file
17
tests/ui/mir/field-projection-mutating-context2.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
static GLOBAL: Mutex<&'static str> = Mutex::new("global str");
|
||||||
|
|
||||||
|
struct Foo<T>(T); // `T` is covariant.
|
||||||
|
|
||||||
|
fn foo<'a>(mut x: Foo<fn(&'a str)>, string: &'a str) {
|
||||||
|
let Foo(ref mut y): Foo<fn(&'static str)> = x;
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
|
*y = |s| *GLOBAL.lock().unwrap() = s;
|
||||||
|
(x.0)(&string);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(Foo(|_| ()), &String::from("i am shortlived"));
|
||||||
|
println!("{}", GLOBAL.lock().unwrap());
|
||||||
|
}
|
10
tests/ui/mir/field-projection-mutating-context2.stderr
Normal file
10
tests/ui/mir/field-projection-mutating-context2.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/field-projection-mutating-context2.rs:8:25
|
||||||
|
|
|
||||||
|
LL | fn foo<'a>(mut x: Foo<fn(&'a str)>, string: &'a str) {
|
||||||
|
| -- lifetime `'a` defined here
|
||||||
|
LL | let Foo(ref mut y): Foo<fn(&'static str)> = x;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
15
tests/ui/mir/field-ty-ascription-enums.rs
Normal file
15
tests/ui/mir/field-ty-ascription-enums.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// build-pass
|
||||||
|
|
||||||
|
enum Foo<T> {
|
||||||
|
Var(T),
|
||||||
|
} // `T` is covariant.
|
||||||
|
|
||||||
|
fn foo<'b>(x: Foo<for<'a> fn(&'a ())>) {
|
||||||
|
let Foo::Var(x): Foo<fn(&'b ())> = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo_nested<'b>(x: Foo<Foo<for<'a> fn(&'a ())>>) {
|
||||||
|
let Foo::Var(Foo::Var(x)): Foo<Foo<fn(&'b ())>> = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
37
tests/ui/mir/field-ty-ascription.rs
Normal file
37
tests/ui/mir/field-ty-ascription.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// build-pass
|
||||||
|
|
||||||
|
struct Foo<T>(T); // `T` is covariant.
|
||||||
|
|
||||||
|
struct Bar<T> {
|
||||||
|
x: T,
|
||||||
|
} // `T` is covariant.
|
||||||
|
|
||||||
|
fn bar<'b>(x: Bar<for<'a> fn(&'a ())>) {
|
||||||
|
let Bar { x }: Bar<fn(&'b ())> = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar_nested<'b>(x: Bar<Bar<for<'a> fn(&'a ())>>) {
|
||||||
|
let Bar { x: Bar { x } }: Bar<Bar<fn(&'b ())>> = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar_foo_nested<'b>(x: Bar<Foo<for<'a> fn(&'a ())>>) {
|
||||||
|
let Bar { x: Foo ( x ) }: Bar<Foo<fn(&'b ())>> = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo<'b>(x: Foo<for<'a> fn(&'a ())>) {
|
||||||
|
let Foo(y): Foo<fn(&'b ())> = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo_nested<'b>(x: Foo<Foo<for<'a> fn(&'a ())>>) {
|
||||||
|
let Foo(Foo(y)): Foo<Foo<fn(&'b ())>> = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tuple<'b>(x: (u32, for<'a> fn(&'a ()))) {
|
||||||
|
let (_, y): (u32, fn(&'b ())) = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tuple_nested<'b>(x: (u32, (u32, for<'a> fn(&'a ())))) {
|
||||||
|
let (_, (_, y)): (u32, (u32, fn(&'b ()))) = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue