Skip to content
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

Rust can't figure out that two types are the same with supertraits and associated types. #135979

Open
theemathas opened this issue Jan 24, 2025 · 1 comment
Labels
A-associated-items Area: Associated items (types, constants & functions) A-trait-system Area: Trait system C-bug Category: This is a bug. F-associated_type_bounds `#![feature(associated_type_bounds)]` T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@theemathas
Copy link
Contributor

theemathas commented Jan 24, 2025

I tried this code:

trait Foo {
    type ToBar;
}

trait Bar {
    type ToFoo: Foo<ToBar = Self>;
}

trait SubFoo: Foo {
    type SubToBar: Bar<ToFoo = Self>;
}

fn works<F: SubFoo>(x: F::SubToBar) -> F::ToBar {
    fn helper<B: Bar>(x: B) -> <B::ToFoo as Foo>::ToBar {
        x
    }

    helper::<F::SubToBar>(x)
}

fn fails<F: SubFoo>(x: F::SubToBar) -> F::ToBar {
    x
}

I expected the code to compile. However, the works function compiles, while the fails function fails to compile, producing the error:

error[E0308]: mismatched types
  --> src/lib.rs:22:5
   |
21 | fn fails<F: SubFoo>(x: F::SubToBar) -> F::ToBar {
   |                                        -------- expected `<F as Foo>::ToBar` because of return type
22 |     x
   |     ^ expected `Foo::ToBar`, found `SubFoo::SubToBar`
   |
   = note: expected associated type `<F as Foo>::ToBar`
              found associated type `<F as SubFoo>::SubToBar`
   = note: an associated type was expected, but a different one was found

For more information about this error, try `rustc --explain E0308`.

For every SubFoo type, the ToBar and SubToBar associated types will always be the same (Self::ToBar == Self::SubToBar). This can be proven from by the following chain of reasoning for any type F: SubFoo:

<F as SubFoo>::SubToBar
== <<<F as SubFoo>::SubToBar as Bar>::ToFoo as Foo>::ToBar
(from applying the trait bound  `ToFoo: Foo<ToBar = Self>`  to the type  `<F as SubFoo>::SubToBar`)
== <F as Foo>::ToBar
(from applying the trait bound  `SubToBar: Bar<ToFoo = Self>` to the type  `F`)

It seems like the trait solver can't figure this out by itself. But prodding it with the helper() function makes it able to figure this out. This workaround is rather inconvenient though, and it would be nice if the trait solver can figure this out itself, or if there were some way to give hints to the trait solver to reach this conclusion.

Note: my attempts to simplify things have repeatedly ran into the problem described at #65913. Also, the original use case had GATs, which made associated type bounds not usable on the Foo/SubFoo traits.

Meta

Reproducible on the playground with stable rust version 1.84.0, and nightly rust version 1.86.0-nightly (2025-01-23 99768c80a1c094a5cfc3)

Using -Z next-solver=globally doesn't fix the problem.

@rustbot labels +A-trait-system +A-associated-items

@theemathas theemathas added the C-bug Category: This is a bug. label Jan 24, 2025
@rustbot rustbot added needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. A-associated-items Area: Associated items (types, constants & functions) A-trait-system Area: Trait system F-associated_type_bounds `#![feature(associated_type_bounds)]` labels Jan 24, 2025
@jieyouxu jieyouxu added the T-types Relevant to the types team, which will review and decide on the PR/issue. label Jan 24, 2025
@theemathas theemathas changed the title Rust can't figure out that two types are the same with supertraits containing associated type bounds. Rust can't figure out that two types are the same with supertraits and associated types. Jan 24, 2025
@BoxyUwU
Copy link
Member

BoxyUwU commented Jan 24, 2025

Given some F::SubToBar eq F::ToBar goal the way to prove this in theory would be to normalize F::ToBar to F::SubToBar based on:

  • F::SubToBar implies F::SubToBar: Bar<ToFoo = F>
  • F::SubToBar: Bar implies F::SubToBar: Bar<ToFoo: Foo<ToBar = F::SubToBar>
  • in some imaginary world you could use that to imply F: Foo<ToBar = F::SubToBar>

Something like assembling all alias bounds for all aliases involved in a goal and adding them to the environment or something like that (?). Seems scary and at odds with the decision to only use alias bounds from rigid aliases.

Alternatively special casing stuff such that a T: Trait<Assoc = F> bound will imply all the item bounds of Assoc on F, I remember us talking about potentially doing something similarly for opaque types so that you don't wind up with impl Trait<Assoc = impl Sized> where Assoc: Clone is required but the caller can't prove it.

That also seems scary 🤔 if you had something like type Assoc: Trait<Assoc = Self> where /* unsatisfiable */ handling that properly seems tricky especially once GATs get involved


In the original example adding a Foo<ToBar = Self::SubToBar> supertrait to SubBar fixes the example, although I've been told that in the unminimized example GATs are being used so it would actually require a bound such as Foo<for<T> ToBar<T> = Self::SubToBar<T>> which is quite a ways off. However, that seems like a nicer way for this to compile than making item bounds/alias bounds even more complex and is probably something we'll support eventually anyway

@BoxyUwU BoxyUwU removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jan 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items (types, constants & functions) A-trait-system Area: Trait system C-bug Category: This is a bug. F-associated_type_bounds `#![feature(associated_type_bounds)]` T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants