Change:
So pointed me at this : There's a lot going on here, but the key point is that, in the call to , we assume that . This is reasonable, since it's a trait object type -- however, it is a malformed trait object type, and hence one where no impl could ever exist (i.e., we could never make an instance of this type). Interestingly, we are not smart enough to extend this to the case where the bound is applied directly (hence the commented out call to . At the very least, this is inconsistent behavior, but I feel like there is an unsoundness lurking here. What makes me nervous is that we say that a projection type outlives if for all . I am worried that you could use an impl like this: to prove then that outlives (say). I have not yet quite managed to weaponize this, but I got -- that's a gist in which we invoke a function where will be of type when monomophized. If rustc were as smart as chalk, I suspect we could easily craft a transmute of some kind to . There may be a way to do it within current rustc too. I'm not sure what's the best fix here. I suspect we should require that trait object types meet the WF requirements declared on the trait; and yet, I think the reason we did not is because we don't know the type, which means we can't fully validate that. =) This also feels a mite inconsistent with implied bounds. (Perhaps most likely is that we could tweak the WF rules for trait objects to specifically target lifetime bounds, but I have to think about that, feels .. hacky?) We could tweak the notion of what type parameters are constrained -- or at least which may appear in a projection. e.g., we could disallow that impl of for , because would not be considered "sufficiently" constrained to appear in . Backwards incompatible, obviously, and feels unfortunate. This causes , as discovered by in ; cc <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOT_END --
Actually, thinking about this more, the problem is maybe not quite how I was thinking of it. That is, it seems like the problem lies in the definition of object safety. That is, if you imagine that you had to write the impl for by hand, it would look like: and this would fail the well-formedness checks because does not hold, at least not without relying on the impl we are writing =). The goal of the object safety rules are to ensure such an impl could be written. Maybe the rules of object safety have to be more conditional -- i.e., is object safe if .
Here is an actual unsoundness, using :
This looks similar to - in both cases we have a trait object that is not WF. I think a reasonable way to solve this would be to have a trait object predicate depend on a predicate, so we basically have the rule
The "this part is new" part is actually more subtle than I realized. Object types (aka , to avoid confusion) are actually a funny way of writing And the idea is that if the trait is object-safe, we can write a "compiler impl" If we want to write such an impl, we need the where-clauses on the trait (both associated types and others) to hold. For supertraits, this follows by obvious induction. For other constraints, this is something that won't hold by itself. Therefore, we probably need to add these other predicates as conditions on the impl, and have
I agree that it is the same as . As we said on IRC, I think that the current fix is roughly -- in particular, when we check that , we need to also check that the where-clauses declared on hold for . This subsumes some of the object safety checks (e.g. the search for ).
Shall we close this as a dupe, then?
ping -- whatever happened with your branch here? I remember we had some discussions about cycles and don't recall how (if at all) they were resolved.
Marking P-medium. While there's overlap with , this issue has more in-depth information. We plan to keep them both open until fixed.
Since was fixed, can this issue be closed?
We agreed that this issue and shared a similar root cause, but my still compiles on both stable and nightly and is still unsound. I did not know about until now, but it seems to only have fixed the case of when is supposed to be . It’s quite obvious why this case was incorrect in the first place. Conversely, it’s less obvious why there should be a problem with in my initial example. After all, it’s possible to implement for . The reason being that lifetime requirements are syntactic and if you allow in conjunction with implied bounds, you can derive contradictory things. It’s the same reason why the type is not considered to outlive , even if the underlying object is just a function pointer. describes a formal solution under the full implied bounds setting (it would need to be slightly adapted for ). I think an easy fix with the current limited form of implied bounds would just be to collect all lifetimes appearing in , and check that they verify the outlive clauses transitively required by . If the check fails, we say that is not well-formed. I believe this would be sufficient to fix the soundness bug.
another repro for the same issue (i think)
claim
We looked at this in the types meeting -- none of the the existing reproductions compile anymore -- but we're not sure if this is truly fixed or not. It's not clear what made them stop compile. A good first step would be bisecting to see what PR fixed this example:
searched nightlies: from nightly-2020-01-01 to nightly-2022-11-30 regressed nightly: nightly-2022-08-10 searched commit range: regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.4</summaryHost triple: x86_64-unknown-linux-gnu Reproduce with: </detailsso it was fixed by
it looks like the original unsoundness was fixed before , it seems like that stopped compiling in 1.61. can I get you to bisect this one as well?
I got , which maps to Unfortunately, there is no CI builds anymore for this range, and I do not have the power to bisect those commit myself now. Reproduce with
I'm guessing it was
that's cool, looks like we only need the add ui tests and can then close this issue

Discussion:
<ins> // Taken from https://github.com/rust-lang/rust/issues/44454#issue-256435333 trait Animal<X>: 'static {} fn foo<Y, X>() where Y: Animal<X> + ?Sized, { // `Y` implements `Animal<X>` so `Y` is 'static. baz::<Y>() } fn bar<'a>(_arg: &'a i32) { foo::<dyn Animal<&'a i32>, &'a i32>() //~ ERROR: lifetime may not live long enough } fn baz<T: 'static + ?Sized>() {} fn main() { let a = 5; bar(&a); } </ins>

<ins> error: lifetime may not live long enough --> $DIR/issue-44454-1.rs:14:5 | LL | fn bar<'a>(_arg: &'a i32) { |        -- lifetime `'a` defined here LL |     foo::<dyn Animal<&'a i32>, &'a i32>() |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error: aborting due to previous error </ins>

<ins> // Taken from https://github.com/rust-lang/rust/issues/44454#issuecomment-1175925928 trait Trait<ARG: 'static>: 'static { type Assoc: AsRef<str>; } fn hr<T: ?Sized, ARG>(x: T::Assoc) -> Box<dyn AsRef<str> + 'static> where T: Trait<ARG> { Box::new(x) } fn extend_lt<'a>(x: &'a str) -> Box<dyn AsRef<str> + 'static> { type DynTrait = dyn for<'a> Trait<&'a str, Assoc = &'a str>; hr::<DynTrait, _>(x) //~ ERROR: borrowed data escapes outside of function } fn main() { let extended = extend_lt(&String::from("hello")); println!("{}", extended.as_ref().as_ref()); } </ins>

<ins> error[E0521]: borrowed data escapes outside of function --> $DIR/issue-44454-2.rs:16:5 | LL | fn extend_lt<'a>(x: &'a str) -> Box<dyn AsRef<str> + 'static> { |              --  - `x` is a reference that is only valid in the function body |              | |              lifetime `'a` defined here LL |     type DynTrait = dyn for<'a> Trait<&'a str, Assoc = &'a str>; LL |     hr::<DynTrait, _>(x) |     ^^^^^^^^^^^^^^^^^^^^ |     | |     `x` escapes the function body here |     argument requires that `'a` must outlive `'static` error: aborting due to previous error For more information about this error, try `rustc --explain E0521`. </ins>

<ins> // Taken from https://github.com/rust-lang/rust/issues/44454#issuecomment-1332781290 use std::any::Any; trait Animal<X>: 'static {} trait Projector { type Foo; } impl<X> Projector for dyn Animal<X> { type Foo = X; } fn make_static<'a, T>(t: &'a T) -> &'static T { let x: <dyn Animal<&'a T> as Projector>::Foo = t; let any = generic::<dyn Animal<&'a T>, &'a T>(x); //~^ ERROR: lifetime may not live long enough any.downcast_ref::<&'static T>().unwrap() } fn generic<T: Projector + Animal<U> + ?Sized, U>(x: <T as Projector>::Foo) -> Box<dyn Any> { make_static_any(x) } fn make_static_any<U: 'static>(u: U) -> Box<dyn Any> { Box::new(u) } fn main() { let a = make_static(&"salut".to_string()); println!("{}", *a); } </ins>

<ins> error: lifetime may not live long enough --> $DIR/issue-44454-3.rs:17:15 | LL | fn make_static<'a, T>(t: &'a T) -> &'static T { |                -- lifetime `'a` defined here LL |     let x: <dyn Animal<&'a T> as Projector>::Foo = t; LL |     let any = generic::<dyn Animal<&'a T>, &'a T>(x); |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` error: aborting due to previous error </ins>