With the following code:
trait Trait {
type Assoc;
}
async fn foo<T: Trait<Assoc = ()>>() -> T::Assoc {
()
}
comes the following error:
error[E0271]: type mismatch resolving `<impl std::future::Future as std::future::Future>::Output == <T as Trait>::Assoc`
--> src/lib.rs:7:41
|
7 | async fn foo<T: Trait<Assoc = ()>>() -> T::Assoc {
| ^^^^^^^^ expected (), found associated type
|
= note: expected type `()`
found type `<T as Trait>::Assoc`
= note: the return type of a function must have a statically known size
error: aborting due to previous error
However, this function compiles correctly without the async keyword.
You can trigger this without async/await using impl Trait:
struct Foo<T>(T);
trait FooLike { type Output; }
impl<T> FooLike for Foo<T> {
type Output = T;
}
trait Trait {
type Assoc;
}
fn foo<T: Trait<Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
Foo(())
}
error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as Trait>::Assoc`
--> src/main.rs:13:35
|
13 | fn foo<T: Trait<Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found associated type
|
= note: expected type `()`
found type `<T as Trait>::Assoc`
= note: the return type of a function must have a statically known size
cc @oli-obk @nikomatsakis
Some more reduction in genericity and clearing up a few things:
#![feature(impl_trait_in_bindings)]
struct Foo;
trait FooLike { type Output; }
impl FooLike for Foo {
type Output = u32;
}
trait Trait {
type Assoc;
}
fn foo<T: Trait<Assoc = i32>>() {
let _: impl FooLike<Output = T::Assoc> = Foo;
}
error[E0271]: type mismatch resolving `<Foo as FooLike>::Output == <T as Trait>::Assoc`
--> src/main.rs:15:12
|
15 | let _: impl FooLike<Output = T::Assoc> = Foo;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found associated type
|
= note: expected type `u32`
found type `<T as Trait>::Assoc`
= note: the return type of a function must have a statically known size
The problem is that T::Assoc = i32 is not know in impl trait items. It's also not related to the return position, but refers to any impl trait in the function. Outside of impl trait T::Assoc = i32 can be resolved without a problem, so it must be something related to the way impl trait reuses the generics of its parent.
It seems to be a missing normalization, most likely.
I have a local fix for @cramertj's reduction above, but https://github.com/rust-lang/rust/issues/61577 is making this rather difficult as it makes debug! output not work properly.
This also happens for existential type definitions:
#![feature(existential_type)]
trait Implemented {
type Assoc;
}
impl<T> Implemented for T {
type Assoc = u8;
}
trait Trait {
type Out;
}
impl Trait for () {
type Out = u8;
}
existential type Ex: Trait<Out = <() as Implemented>::Assoc>;
fn define() -> Ex {
()
}
error[E0271]: type mismatch resolving `<() as Trait>::Out == <() as Implemented>::Assoc`
--> src/lib.rs:18:1
|
18 | existential type Ex: Trait<Out = <() as Implemented>::Assoc>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found associated type
|
= note: expected type `u8`
found type `<() as Implemented>::Assoc`
= note: the return type of a function must have a statically known size
My fix only seems to turn this into a cycle error for some reason.
EDIT: Nevermind I just broke existential types in general lol
Marking as blocking, although I think there's minimal future compat risk. If push came to shove I personally would be ok with removing this from the blocking list. But then it looks like @jonas-schievink already fixed it =)
@jonas-schievink I think the problem is on this line of code:
in particular, we "instantiate" the predicates -- meaning, substitute in the values for their type parameters -- but we never normalize them. We should be able to invoke the normalize methods. Note that in this code we do have an infcx available, as well, in self. I think we should be able to invoke:
self.infcx.partially_normalize_associated_types_in(span, body-id, param_env, &predicates)
this returns an InferOk value, which contains obligations that must be pushed into self.obligations.
@nikomatsakis Hmm, that code is only invoked if opaque_defn.has_required_region_bounds is true, which I think wouldn't be the case here since there are no region bounds at all (right?).
I also noticed that self.obligations does not exist in this context (self is an &InferCtxt), and there's no FulfillmentContext nearby either.
However, another place where predicates_of.instantiate(..) is called without normalizing the result is here, further down in the same file:
...and there self.obligations does exist (as does self.infcx). Quick testing shows that normalizing there fixes this issue as well, and as a bonus also fixes the issue with existential type without ICEs!
@jonas-schievink good catch -- actually I think the lines you found are the ones I meant to direct you to in the first place. I see you have some other problems on the PR, looking now.
Most helpful comment
This also happens for
existential typedefinitions:My fix only seems to turn this into a cycle error for some reason.
EDIT: Nevermind I just broke existential types in general lol