-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Returning an impl Trait
in associated type position results in error at point-of-use instead of point-of-definition
#72614
Comments
@rustbot modify labels to +A-impl-trait |
Fixed in 1.49.0 @rustbot label E-needs-test |
So the problem here was diagnostics, right? Since it now compiles fine, I think it's more of "hidden" than "fixed". Given that, I'm not sure if it's really worth adding a regression test, any thoughts? |
Wait, how does this compile now? A slight modification shows that it's now somehow leaking additional trait impls that weren't declared on the pub trait Foo {
type Bar: std::ops::Add<Self::Bar, Output = Self::Bar> + std::fmt::Debug;
fn bar(self) -> Self::Bar;
}
impl<T: std::ops::Add<T, Output = T> + std::fmt::Debug> Foo for T {
type Bar = T;
fn bar(self) -> Self::Bar {
self
}
}
pub fn foo() -> impl Foo<Bar = impl std::ops::Sub<usize>> {
5usize
}
fn baz<T: Foo>(foo1: T, foo2: T) { dbg!(foo1.bar() + foo2.bar()); }
fn main() {
baz(foo(), foo());
} |
Yea, this is pretty much that one backcompat hack that I want to eliminate:
|
@oli-obk thanks for the pointer! Are we ready to run crater or do you want to add some more patches before it? |
Unless something comes up during the removal PR itself, this should be ready to go |
Fixed by #73905 specifically. |
The reason this works is that in the original example we replace all opaque types in the function signature with inference variables (for typeck, nowhere else!). So in pub fn foo() -> impl Foo<Bar = impl std::ops::Sub<usize>> {
5usize
}
now... during typeck we figure out that the return type of the function is This means that we now have to prove Luckily that is easy to prove, as there's impl<T: std::ops::Add<T>> Foo for T {
type Bar = T;
fn bar(self) -> Self::Bar {
self
}
} that Due to that impl we know
Due to our obligations we know
makes us know that Furthermore we got So this passes compilation so far. Now the question is why |
Thanks for the detailed explanation, Oli! I've now understood it :) |
I don't understand why it is allowed to bring this additional information in:
The type |
There is no leaking happening. There is a weird situation where as long as you only know about the associated type, but not what it resolves to, you know about the Add but not the Sub. When you normalize the assoc type to its concrete type, you lose the information about the Add but gain the Sub. |
… r=oli-obk Remove a back-compat hack on lazy TAIT This PR's motivation is here: rust-lang#72614 (comment) ~~But removing a hack doesn't seem to reject the code on the issue, there're some more hacks?~~ r? `@oli-obk`
… r=oli-obk Remove a back-compat hack on lazy TAIT This PR's motivation is here: rust-lang#72614 (comment) ~~But removing a hack doesn't seem to reject the code on the issue, there're some more hacks?~~ r? ``@oli-obk``
Example (playground):
This should error at the definition of
foo
sinceimpl std::ops::Sub<usize>
does not satisfy thestd::ops::Add
bound required byFoo::Bar
. Instead it errors at the use-site inmain
, and if that line is deleted (e.g. if this is apub fn
in a library) there is no error.The text was updated successfully, but these errors were encountered: