Description
I tried this code (which is actually the minimum reproduction I could find, as the bug seems to disappear when I try to reduce it more):
use std::marker::PhantomData;
pub struct XImpl<T, E, F2, F1>
where
F2: Fn(E),
{
f1: F1,
f2: F2,
_ghost: PhantomData<(T, E)>,
}
pub trait X<T>: Sized {
type F1;
type F2: Fn(Self::E);
type E;
fn and<NewF1, NewF1Generator>(self, f: NewF1Generator) -> XImpl<T, Self::E, Self::F2, NewF1>
where
NewF1Generator: FnOnce(Self::F1) -> NewF1;
}
impl<T, E, F2, F1> X<T> for XImpl<T, E, F2, F1>
where
F2: Fn(E),
{
type E = E;
type F2 = F2;
type F1 = F1;
fn and<NewF1, NewF1Generator>(self, f: NewF1Generator) -> XImpl<T, E, F2, NewF1>
where
NewF1Generator: FnOnce(F1) -> NewF1,
{
XImpl {
f1: f(self.f1),
f2: self.f2,
_ghost: PhantomData,
}
}
}
fn f() -> impl X<()> {
XImpl {
f1: || (),
f2: |()| (),
_ghost: PhantomData,
}
}
fn f2() -> impl X<()> {
f().and(|rb| rb)
}
It does compile: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=54e239ced207241c8438dddbc325ef19
If however we specify the value for the associated type E
in f
and f2
, so that code calling f2()
can depend on knowing the type E
:
fn f() -> impl X<(), E = ()> {
XImpl {
f1: || (),
f2: |()| (),
_ghost: PhantomData,
}
}
fn f2() -> impl X<(), E = ()> {
f().and(|rb| rb)
}
The compiler seems to forget that any X::F2
implements Fn(Self::E)
, and stops compiling: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1e84aea4402bd45da498aace9682b1ad
Compiling playground v0.0.1 (/playground)
error[E0277]: expected a `std::ops::Fn<((),)>` closure, found `<impl X<()> as X<()>>::F2`
--> src/lib.rs:51:6
|
51 | f().and(|rb| rb)
| ^^^ expected an `Fn<((),)>` closure, found `<impl X<()> as X<()>>::F2`
|
= help: the trait `std::ops::Fn<((),)>` is not implemented for `<impl X<()> as X<()>>::F2`
error[E0277]: expected a `std::ops::Fn<((),)>` closure, found `<impl X<()> as X<()>>::F2`
--> src/lib.rs:51:2
|
12 | pub trait X<T>: Sized {
| --------------------- required by `X`
...
51 | f().and(|rb| rb)
| ^^^^^^^^^^^^^^^^ expected an `Fn<((),)>` closure, found `<impl X<()> as X<()>>::F2`
|
= help: the trait `std::ops::Fn<((),)>` is not implemented for `<impl X<()> as X<()>>::F2`
error[E0277]: expected a `std::ops::Fn<((),)>` closure, found `<impl X<()> as X<()>>::F2`
--> src/lib.rs:50:12
|
50 | fn f2() -> impl X<(), E = ()> {
| ^^^^^^^^^^^^^^^^^^ expected an `Fn<((),)>` closure, found `<impl X<()> as X<()>>::F2`
51 | f().and(|rb| rb)
| ---------------- this returned value is of type `XImpl<(), (), <impl X<()> as X<()>>::F2, <impl X<()> as X<()>>::F1>`
|
= help: the trait `std::ops::Fn<((),)>` is not implemented for `<impl X<()> as X<()>>::F2`
= note: required because of the requirements on the impl of `X<()>` for `XImpl<(), (), <impl X<()> as X<()>>::F2, <impl X<()> as X<()>>::F1>`
= note: the return type of a function must have a statically known size
Note that the bug does not happen if F2: Fn()
instead of F2: Fn(E)
:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=266f91f2ba2f0730148c4dea8c3732a4
It does not happen either if F2: Fn(T)
instead of F2: Fn(E)
:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e37b04bfdeec2a1ebddc10a4422931e8
Meta
rustc --version --verbose
:
rustc 1.43.1 (8d69840ab 2020-05-04)
binary: rustc
commit-hash: 8d69840ab92ea7f4d323420088dd8c9775f180cd
commit-date: 2020-05-04
host: x86_64-unknown-linux-gnu
release: 1.43.1
LLVM version: 9.0
This also happens on nightly.