Skip to content

Specifying one associated type makes Rust forget about the constraint on the other ones #72207

Open
@Ten0

Description

@Ten0

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)A-closuresArea: Closures (`|…| { … }`)A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.A-trait-systemArea: Trait systemC-bugCategory: This is a bug.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions