Remove 'static requirement on try_as_dyn#150161
Remove 'static requirement on try_as_dyn#150161oli-obk wants to merge 2 commits intorust-lang:mainfrom
Conversation
|
r? @SparrowLii rustbot has assigned @SparrowLii. Use |
This comment has been minimized.
This comment has been minimized.
e7ef1ee to
29f1dba
Compare
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.
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.
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.
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.
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 comment has been minimized.
This comment has been minimized.
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.
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.
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.
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 |
This comment was marked as resolved.
This comment was marked as resolved.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
544129f to
c25c968
Compare
|
|
||
| fn impl_is_fully_generic_for_reflection(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { | ||
| tcx.impl_trait_header(def_id).is_fully_generic_for_reflection() | ||
| && tcx.explicit_predicates_of(def_id).is_fully_generic_for_reflection() |
There was a problem hiding this comment.
does this mean we dont handle elaborated supertraits? e.g.
trait Trait: Super<'static> {}
trait Other<T> {}
impl<T: Trait> Other<T> for Foo {}is the impl of Other considered to be fully generic for reflection
There was a problem hiding this comment.
I think that this is probably fine. It can't cause the answer to "does Foo implement Other" to depend on lifetimes, since satisfying the impl block's where bounds is a necessary and sufficient condition for implementing the subtrait. If the fully generic where bounds didn't imply implementing the supertrait already, then we would have already errored in the impl block.
There was a problem hiding this comment.
There could plausibly be some problems involving inductive cycles that don't go through an impl block for the supertrait, but I don't think that could cause trouble, given that try_as_dyn isn't evaluated in a generic context.
There was a problem hiding this comment.
Before the last commit
trait Trait: 'static {}
trait Other {}
struct Foo<T>(T);
impl Trait for () {}
// This impl should not be visible, as it has a `T: 'static` bound
impl<T: Trait> Other for Foo<T> {}
const _: () = {
let foo = Foo(());
assert!(try_as_dyn::<Foo<()>, dyn Other>(&foo).is_none());
};was returning Some, so we definitely need to elaborate.
Similar to how we need to expand type aliases (and not just reject them like I do rn).
There was a problem hiding this comment.
After some back and forth with @theemathas I have come to the conclusion that we do not need to elaborate bounds. The fact that we restrict all impls that are part of figuring out a Some output of try_as_dyn means that we allow this fully generic impl, but then reject
impl Trait for &'static () {}And you can't cheat and do sth weird like
impl<'a> Trait for &'a () {}because of the trait Trait: 'static {}.
|
|
||
| /// Allow simple where bounds like `T: Debug`, but prevent any kind of | ||
| /// outlives bounds or uses of generic parameters on the right hand side. | ||
| pub fn is_fully_generic_for_reflection(self) -> bool { |
There was a problem hiding this comment.
should see how min specialization handles repeated parameters and reuse that logic ideally
There was a problem hiding this comment.
min spec almost always compares two impls, which we could probably somewhat re-use (the second impl can just be the fake one by the trait which defines its default methods)
Also all the spec logic just errors, while want to silently return None (or Err in the future if we want).
So yea, we can share logic, but it needs a larger refactoring of the spec logic.
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.
|
I haven't given this enough thought to be sure, but I suspect that we can allow all Edit: I've thought about it more. I think that this works, except that we still need to keep prohibiting generics inside |
|
The invariant is that each Edit: A |
|
This code feels really close to an unsoundness. It panics at run time at the unwrap, but I don't understand why. Could you explain how the trait solver manages to correctly figure out that the type doesn't implement the trait? #![feature(try_as_dyn)]
use core::any::try_as_dyn;
trait HasAssoc<'a> {
type Assoc;
}
struct Dummy;
impl<'a> HasAssoc<'a> for Dummy {
// Changing this to &'a i64 makes the code not panic
type Assoc = &'static i64;
}
trait Trait {}
impl Trait for i32 where for<'a> Dummy: HasAssoc<'a, Assoc = &'a i64> {}
fn main() {
let x = 1i32;
let _: &dyn Trait = try_as_dyn(&x).unwrap();
}Edit: I think it's because the trait solver probably can distinguish bound lifetimes from each other and from 'static |
d08dd2f to
c6f08ed
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. |
c6f08ed to
1bb0baa
Compare
This comment has been minimized.
This comment has been minimized.
1bb0baa to
d2cc847
Compare
|
The job Click to see the possible cause of the failure (guessed by this bot) |
|
Here are the current rules for what counts as a "fully generic impl". I think they should be documented in the public API doc comments. For the purposes of defining fully generic impls, we'll define the following:
An
My proposed rule change replaces the last two bullet points with the following, which I think makes the rules easier to understand:
Edit: As per @danielhenrymantilla's suggestion below, I think that restrictions involving lifetime-infected parameters in |
|
I would like to rename "fully generic impl" to "lifetime-independent impl". |
|
💯 I was gonna suggest just that, @theemathas: a lifetime-independent, or rather, lifetime-agnostic, impl. If using "fully", I think the term used historically is that of it being a blanket impl, so "fully blanket over lifetimes". |
|
We could also try and expand on "exploit" scenarios for each of these rules, should it be missing, that is, some form of rationale as to why each of these rules is (plausibly) necessary (the whole other question being whether these rules are sufficient for soundness, which currently stems from "we haven't been able to fathom other ways to amount to lifetime discrimination"). Plausible reasons for the necessity of these rulesImportant The main problem of
Footnotes
|
|
I agree with @danielhenrymantilla. The repetition restriction on The problem with repeating lifetime-infected parameters is that, when figuring out the parameter substitutions for any given impl, we need to unify the "user-specified types" with the In contrast, repeating lifetime-infected parameters in a |
View all comments
tracking issue: #144361
cc @ivarflakstad @izagawd