-
-
Notifications
You must be signed in to change notification settings - Fork 14.4k
Remove 'static requirement on try_as_dyn #150161
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
base: main
Are you sure you want to change the base?
Conversation
|
r? @SparrowLii rustbot has assigned @SparrowLii. Use |
This comment has been minimized.
This comment has been minimized.
e7ef1ee to
29f1dba
Compare
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.
Some drive-by comments; but I'm not rustc/HIR-savy, so take these with a grain of salt 🙇
tests/ui/any/non_static.rs
Outdated
| assert!(std::any::try_as_dyn::<_, dyn Trait>(&()).is_some()); | ||
| assert!(std::any::try_as_dyn::<_, dyn Trait>(&&[()]).is_some()); | ||
| assert!(std::any::try_as_dyn::<_, dyn Trait>(&&()).is_some()); | ||
| assert!(std::any::try_as_dyn::<_, dyn Trait>(&&42_u32).is_none()); |
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.
This one is a "regression", right? With the old design we could be involving a &'static u32 which is both : 'static, and implementing Trait in the classical sense. Perhaps we'll want a try_as_dyn_static() function as well, for those interested in this use case?
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.
I'll add this to the tracking issue
| if let TypingMode::Reflection = ecx.typing_mode() | ||
| && !cx.is_fully_generic_for_reflection(impl_def_id) | ||
| { | ||
| return Err(NoSolution); |
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.
This is way over my head, lol, but just to sanity-check: could this branch be hoisted to the beginning of the function, as in, any impl that is not fully_generic_for_reflection(), when checking in Reflection mode, ought to result in Err(NoSolution), right? As in, it's not specific to this double-Negative branch?
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.
Well, it's only relevant if the polarities match. Otherwise it's not a match anyway. So while it could be lifted to the front, it's not really necessary either. No strong opinion, but also easily changeable without affecting behavior
|
r? BoxyUwU |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
29f1dba to
fe33b0c
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
175bf5b to
dfa5c33
Compare
dfa5c33 to
30f5641
Compare
|
|
||
| fn extend(a: &Payload) -> &'static Payload { | ||
| // TODO: should panic at the `unwrap` here | ||
| let b: &(dyn Trait + 'static) = try_as_dyn::<&Payload, dyn Trait + 'static>(&a).unwrap(); |
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.
I don't have a solution for this yet. The easy hammer is to reject traits that have such methods. Nicer would be to figure out how to make a bound that ensures the dyn Trait's lifetimes are shorter than the arguments'
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.
Can we check during mir_typeck that the source type outlives the lifetime of the trait object type?
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.
ah its not the intrinsic 🤔 yeah thats tricky
This comment has been minimized.
This comment has been minimized.
|
The hacky solution is obviously not a general fix. But I think it's progress. As a next step I will add the input type as a generic parameter on |
|
The current PR state is still unsound, because it doesn't check #![feature(try_as_dyn)]
use std::any::try_as_dyn;
type Payload = Box<i32>;
trait Super {
fn as_static(&self) -> &'static Payload
where
Self: 'static;
}
trait Sub: Super {}
impl<'a> Super for &'a Payload {
fn as_static(&self) -> &'static Payload
where
Self: 'static,
{
*self
}
}
impl<'a> Sub for &'a Payload {}
fn main() {
let storage: Box<Payload> = Box::new(Box::new(1i32));
let wrong: &'static Payload = extend(&*storage);
drop(storage);
println!("{wrong}");
}
fn extend(a: &Payload) -> &'static Payload {
let b: &(dyn Sub + 'static) = try_as_dyn::<&Payload, dyn Sub + 'static>(&a).unwrap();
b.as_static()
} |
|
The PR is also unsound in another way: It does not check for the existence of an implied #![feature(try_as_dyn)]
use std::any::try_as_dyn;
use dyn_clone::{DynClone, clone_box};
type Payload = Box<i32>;
trait Trait: DynClone {
fn as_static(&'static self) -> &'static Payload; // &'static self has an implied Self: 'static bound
}
impl<'a> Trait for &'a Payload {
fn as_static(&'static self) -> &'static Payload {
*self
}
}
fn extend<'a>(x: &'a Payload) -> &'static Payload {
let dyn_ref: &(dyn Trait + 'static) =
try_as_dyn::<&'a Payload, dyn Trait + 'static>(&x).unwrap();
let dyn_box: Box<dyn Trait + 'static> = clone_box(dyn_ref);
let dyn_static: &'static (dyn Trait + 'static) = Box::leak(dyn_box);
dyn_static.as_static()
}
fn main() {
let storage: Box<Payload> = Box::new(Box::new(1i32));
let wrong: &'static Payload = extend(&*storage);
drop(storage);
println!("{wrong}");
} |
|
Another unsoundness: Seems like some lifetimes were not checked? The following code segfaults with this PR. #![feature(try_as_dyn)]
use std::any::try_as_dyn;
type Payload = Box<i32>;
trait Trait<'a> {
fn as_payload(&self) -> &'a Payload;
}
impl<'a> Trait<'a> for &'a Payload {
fn as_payload(&self) -> &'a Payload {
*self
}
}
fn extend<'a>(x: &'a Payload) -> &'static Payload {
let dyn_ref: &(dyn Trait<'static> + 'static) =
try_as_dyn::<&'a Payload, dyn Trait<'static> + 'static>(&x).unwrap();
dyn_ref.as_payload()
}
fn main() {
let storage: Box<Payload> = Box::new(Box::new(1i32));
let wrong: &'static Payload = extend(&*storage);
drop(storage);
println!("{wrong}");
} |
|
Yes I know 😅 its why I wrote it's not a fix. All the examples you are giving would be fixed by actually checking lifetimes at the call site, which I'm planning as per my next comment |
Why is an "outlives" requirement sufficient, as opposed to a "lifetimes are all equal to each other" requirement? |
tracking issue: #144361
cc @theemathas can you find an unsoundness when combined with generic impls where there are no lifetimes, but reference other impls that have lifetimes with constraints? I couldn't find anything.
cc @ivarflakstad @izagawd