Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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