-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use covariance on type relations of field projection types if possible #107969
Merged
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() {} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's
NonUseContext::AscribeUserTy
which I feel should maybe be invariant 🤔I can also imagine us adding
NonMutatingUse
in the future which should be invariant. Can you also exhaustively match on theNonMutatingUseContext
and probably make allNonUse
invariant. Don't know how to write an example whereCovariance
is needed here.Considering that covariance can be unsound if used incorrectly, we should be as defensive as possible here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late answer.
We would need
NonUseContext::AscribeUserTy
for field projections on enums in match statements where we always insert user type projections I believe, e.g.would not compile if we would use
ty::Invariant
onNonUse(AscribeUserTy)
.I agree that we should be very conservative here, but I also can't see how using covariance here might be unsound. Can you show that it is? I've kept this as covariant for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my worry is that we use
AscribeUserTy
in a position which isn't covariant. E.g. only for arguments of a function we're calling. Thinking more about it I actually can't imagine that happening 🤔👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
AscribeUserType
statement contains a variance. Should it be carried byNonUseContext::AscribeUserTy
for use here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I think so 👍 while I don't think is exploitable as we only use contravariance in
AscribeUserType
for places without any projections. Using the following insuper_ascribe_user_ty
does not cause any failureswe should still add the variance to the use context though as this isn't something I want to implicitly rely on.