Skip to content

Confusing diagnose message in which the opaque types from RPIT are reported as unrelated types #144433

@skr2005

Description

@skr2005

Code

pub struct Bar;

trait BarBar {}
impl BarBar for Bar {}

pub struct Foo<T> {
    t: T,
    bar: Bar,
}

pub type Foo1 = Foo<i32>;
pub type Foo2 = Foo<String>;

pub trait FooFoo {
    fn bar(self) -> impl BarBar;
}

impl<T> FooFoo for Foo<T> {
    fn bar(self) -> impl BarBar {
        self.bar
    }
}

enum Oh {
    FooInt(Foo1),
    FooStr(Foo2),
}

impl FooFoo for Oh {
    fn bar(self) -> impl BarBar {
        use Oh::*;
        match self {
            FooInt(fi) => fi.bar(),
            FooStr(fs) => fs.bar(),
        }
    }
}

Current output

error[E0308]: `match` arms have incompatible types
  --> src\lib.rs:45:27
   |
30 |       fn bar(self) -> impl BarBar {
   |                       -----------
   |                       |
   |                       the expected opaque type
   |                       the found opaque type
...
43 | /         match self {
44 | |             FooInt(fi) => fi.bar(),
   | |                           -------- this is found to be of type `impl BarBar`
45 | |             FooStr(fs) => fs.bar(),
   | |                           ^^^^^^^^ expected `i32`, found `String`
46 | |         }
   | |_________- `match` arms have incompatible types
   |
   = note: expected opaque type `impl BarBar` (`i32`)
              found opaque type `impl BarBar` (`String`)
   = note: distinct uses of `impl Trait` result in different opaque types
help: you could change the return type to be a boxed trait object
   |
41 -     fn bar(self) -> impl BarBar {
41 +     fn bar(self) -> Box<dyn BarBar> {
   |
help: if you change the return type to expect trait objects, box the returned expressions
   |
44 ~             FooInt(fi) => Box::new(fi.bar()),
45 ~             FooStr(fs) => Box::new(fs.bar()),
   |

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

Desired output

// I don't know. I expected the code to compile at first,
// but maybe I am missing something so that's arguable.
// However, anyway, neither fi.bar() is i32 nor fs.bar() is String, so the diagnose parts:
fs.bar(),
^^^^^^^^ expected `i32`, found `String`
// ...and:
= note: expected opaque type `impl BarBar` (`i32`)
           found opaque type `impl BarBar` (`String`)
// seem confusing to me.

Rationale and extra context

Why I expected the code to compile:

In my understanding, RPITIT is a sugar for associated types. So I tried the "desugared" code and that compiles:

pub struct Bar;

trait BarBar {}
impl BarBar for Bar {}

pub struct Foo<T> {
    t: T,
    bar: Bar,
}

pub type Foo1 = Foo<i32>;
pub type Foo2 = Foo<String>;

pub trait FooFoo {
    type BarBarT: BarBar;
    fn bar(self) -> Self::BarBarT;
}

impl<T> FooFoo for Foo<T> {
    type BarBarT = Bar;
    fn bar(self) -> Bar {
        self.bar
    }
}

enum Oh {
    FooInt(Foo1),
    FooStr(Foo2),
}

impl FooFoo for Oh {
    type BarBarT = Bar;
    fn bar(self) -> Bar {
        use Oh::*;
        match self {
            FooInt(fi) => fi.bar(),
            FooStr(fs) => fs.bar(),
        }
    }
}

But the code using RPITIT doesn't. I don't know what is going on there.

Other cases

I also tried this code:

pub struct Bar;

trait BarBar {}
impl BarBar for Bar {}

pub struct Foo<T> {
    t: T,
    bar: Bar,
}

pub type Foo1 = Foo<i32>;
pub type Foo2 = Foo<String>;

pub trait FooFoo {
    fn bar(self) -> impl BarBar;
}

impl FooFoo for Foo1 {
    fn bar(self) -> impl BarBar {
        self.bar
    }
}
impl FooFoo for Foo2 {
    fn bar(self) -> impl BarBar {
        self.bar
    }
}

enum Oh {
    FooInt(Foo1),
    FooStr(Foo2),
}

impl FooFoo for Oh {
    fn bar(self) -> impl BarBar {
        use Oh::*;
        match self {
            FooInt(fi) => fi.bar(),
            FooStr(fs) => fs.bar(),
        }
    }
}

And the code above cannot compile either:

error[E0308]: `match` arms have incompatible types
  --> src\lib.rs:39:27
   |
19 |       fn bar(self) -> impl BarBar {
   |                       ----------- the expected opaque type
...
24 |       fn bar(self) -> impl BarBar {
   |                       ----------- the found opaque type
...
37 | /         match self {
38 | |             FooInt(fi) => fi.bar(),
   | |                           -------- this is found to be of type `impl BarBar`
39 | |             FooStr(fs) => fs.bar(),
   | |                           ^^^^^^^^ expected opaque type, found a different opaque type
40 | |         }
   | |_________- `match` arms have incompatible types
   |
   = note: expected opaque type `impl BarBar`
              found opaque type `impl BarBar`
   = note: distinct uses of `impl Trait` result in different opaque types
help: you could change the return type to be a boxed trait object
   |
35 -     fn bar(self) -> impl BarBar {
35 +     fn bar(self) -> Box<dyn BarBar> {
   |
help: if you change the return type to expect trait objects, box the returned expressions
   |
38 ~             FooInt(fi) => Box::new(fi.bar()),
39 ~             FooStr(fs) => Box::new(fs.bar()),
   |

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

However this case above makes sense for me since there are separate trait implementations. The diagnose message is also clear.

Edit:
I also came up with a case which does not require RPITIT, just RPIT. The case below gives a similar error message to the "Current output" section.

pub struct Bar;

trait BarBar {}
impl BarBar for Bar {}

pub struct Foo<T> {
    t: T,
    bar: Bar,
}

pub type Foo1 = Foo<i32>;
pub type Foo2 = Foo<String>;

pub trait FooFoo {
    fn bar(self) -> impl BarBar;
}

fn bar<T>(f: Foo<T>) -> impl BarBar {
    f.bar
}

enum Oh {
    FooInt(Foo1),
    FooStr(Foo2),
}

fn oh_bar(oh: Oh) -> impl BarBar {
    use Oh::*;
    match oh {
        FooInt(fi) => bar(fi),
        FooStr(fs) => bar(fs),
    }
}

Rust Version

rustc 1.88.0 (6b00bc388 2025-06-23)
binary: rustc
commit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc
commit-date: 2025-06-23
host: x86_64-pc-windows-gnu
release: 1.88.0
LLVM version: 20.1.5

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsT-compilerRelevant to the compiler 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