-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
TAIT regression: requires new lifetime bounds #95922
Comments
I thought this breakage is an unnecessary side effect to the PR, but this maybe the very unsound case that the PR tries to fix 😅 . I'd appreciate a comment from @oli-obk @compiler-errors. If it is unsound, wouldn't that make the following unsound as well? fn call_service<'a, Req>(req: &'a Req) -> impl Future {
async move { let x = req; }
} |
The problem is a bit roundabout. Basically there is an implicit impl<'a, Req> Service<'a, Req> for u8 {
type Future = impl Future;
fn call(req: &'a Req) -> Self::Future {
async move { let x = req; }
}
} is in fact impl<'a, Req> Service<'a, Req> for u8 {
type Future = impl Future;
fn call(req: &'a Req) -> Self::Future where Req: 'a {
async move { let x = req; }
}
} (which is implied from Now, TAIT desugars to putting a Ideally this would just be a |
Nope, that case is easier, because the implicit |
Thanks. So to conclude, this use case is indeed unsound and I'm happy it's now rejected. One problem though I faced while redesigning the trait, TAIT now seems at a disadvantage to writing the full type: Given the following example: trait Service<Req> {
type Output;
fn call(req: Req) -> Self::Output;
}
impl<'a, Req> Service<&'a Req> for u8 {
type Output= impl Copy;
fn call(req: &'a Req) -> Self::Output {
req
}
} This still fails and requires the explicit bound impl<'a, Req> Service<&'a Req> for u8 {
type Output= &'a Req;
fn call(req: &'a Req) -> Self::Output {
req
}
} This is really a deal-breaker for using TAIT because of #95921 Do you think this is an easy problem and we can expect a fix in the near future? |
Yea, so since your service trait now has I'll fix this, but I'm going on vacation end of the week, so you may be in for a bit of a wait |
While digging into this I noticed that Pretty sure I know why, too. This is gonna be a rabbit hole |
Side note: #![feature(type_alias_impl_trait)]
#![feature(generic_associated_types)]
trait Service<Req> {
type Output<'x> where Req: 'x;
fn call<'y>(req: &'y Req) -> Self::Output<'y>;
}
impl<Req> Service<Req> for u8 {
type Output<'b> = impl Copy where Req: 'b;
fn call<'c>(req: &'c Req) -> Self::Output<'c> {
req
}
} may be more what you are looking for, but without knowing more about your use case, I can't be sure. (This issue requires solving regardless, but using a different scheme may unblock you) |
Unfortuanltely, I hit another blocker when using GATs as recommended: #![feature(generic_associated_types)]
#![feature(type_alias_impl_trait)]
use core::ops::Deref;
trait Service3<Req> {
type Output<'a> where Req: 'a;
fn call(req: &mut Req) -> Self::Output<'_>;
}
struct WebReq<'s, S>(&'s S);
impl<'s, S> Service3<WebReq<'s, S>> for u8 {
type Output<'a> = impl Deref where WebReq<'s, S>: 'a;
fn call<'a>(req: &'a mut WebReq<'s, S>) -> Self::Output<'a> {
req
}
} This requires the bound
So again adding the bound to the impl seems to be the only option. |
use implied bounds when checking opaque types During opaque type inference, we check for the well-formedness of the hidden type in the opaque type's own environment, not the one of the defining site, which are different in the case of TAIT. However in the case of associated-type-impl-trait, we don't use implied bounds from the impl header. This caused us to reject the following: ```rust trait Service<Req> { type Output; fn call(req: Req) -> Self::Output; } impl<'a, Req> Service<&'a Req> for u8 { type Output= impl Sized; // we can't prove WF of hidden type `WF(&'a Req)` although it's implied by the impl //~^ ERROR type parameter Req doesn't live long enough fn call(req: &'a Req) -> Self::Output { req } } ``` although adding an explicit bound would make it pass: ```diff - impl<'a, Req> Service<&'a Req> for u8 { + impl<'a, Req> Service<&'a Req> for u8 where Req: 'a, { ``` I believe it should pass as we already allow the concrete type to be used: ```diff impl<'a, Req> Service<&'a Req> for u8 { - type Output= impl Sized; + type Output= &'a Req; ``` Fixes rust-lang#95922 Builds on rust-lang#105982 cc `@lcnr` (because implied bounds) r? `@oli-obk`
Edit: The original example was indeed unsound so it's thankfully rejected now. See this comment for a sound example that gets rejected.
Original unsound code
(Playground)
This code used to compile fine prior to #95519. It now requires an explicit lifetime bound
Req: 'a
to the impl.Unfortunately, adding a lifetime bound may not be an option when used with HRTBS due to limitations like #95921. This caused a major breakage on my part.
Meta
regressed in the nightly version
1.62.0-nightly (f4a7ce997 2022-04-08)
Error Output
The text was updated successfully, but these errors were encountered: