Skip to content

Diagnostic forgets about transitive trait bounds when there are multiple candidate impls. #90665

Open
@BGR360

Description

@BGR360

EDIT this was originally reported as a problem specific to auto-traits, but I don't think it actually is. See the comment below.

EDIT 2: It's actually not specific to specialization either. See this comment

This is spun off from #90601.

Given the following code (playground):

#![feature(min_specialization)]
#![feature(rustc_attrs)]

#[rustc_specialization_trait]
trait Special {}

trait Foo {
    fn foo();
}

impl<T: Send> Foo for T {
    default fn foo() {
        println!("foo");
    }
}

fn main() {
    <std::rc::Rc<()> as Foo>::foo();
}

The current (good) output is:

error[E0277]: `Rc<()>` cannot be sent between threads safely
  --> src/main.rs:18:5
   |
18 |     <std::rc::Rc<()> as Foo>::foo();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc<()>` cannot be sent between threads safely
   |
   = help: the trait `Send` is not implemented for `Rc<()>`
note: required because of the requirements on the impl of `Foo` for `Rc<()>`
  --> src/main.rs:11:15
   |
11 | impl<T: Send> Foo for T {
   |               ^^^     ^
note: required by `Foo::foo`
  --> src/main.rs:8:5
   |
8  |     fn foo();
   |     ^^^^^^^^^

But if you add a specialized impl of Foo (playground):

#![feature(min_specialization)]
#![feature(rustc_attrs)]

#[rustc_specialization_trait]
trait Special {}

trait Foo {
    fn foo();
}

impl<T: Send> Foo for T {
    default fn foo() {
        println!("foo");
    }
}

impl<T: Send + Special> Foo for T {
    fn foo() {
        println!("special foo");
    }
}

fn main() {
    <std::rc::Rc<()> as Foo>::foo();
}

Then the error message loses all of the information regarding the unimplemented auto trait:

error[E0277]: the trait bound `Rc<()>: Foo` is not satisfied
  --> src/main.rs:24:5
   |
24 |     <std::rc::Rc<()> as Foo>::foo();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Rc<()>`
   |
note: required by `Foo::foo`
  --> src/main.rs:8:5
   |
8  |     fn foo();
   |     ^^^^^^^^^

So there is something about the presence of a specialized impl that causes trait selection to not know that there's an unimplemented auto trait obligation. I have confirmed this by exploring the debug logging of rustc a little bit:

Without specialized impl:
rustc_trait_selection::traits::error_reporting::report_fulfillment_error error=FulfillmentError(Obligation(predicate=Binder(TraitPredicate(<std::rc::Rc<()> as std::marker::Send>, polarity:Positive), []), depth=1),Unimplemented), body_id=Some(BodyId { hir_id: HirId { owner: DefId(0:9 ~ issue_xxxxx_specialization[8170]::main), local_id: 12 } }), fallback_has_occurred=false

With specialized impl:
rustc_trait_selection::traits::error_reporting::report_fulfillment_error error=FulfillmentError(Obligation(predicate=Binder(TraitPredicate(<std::rc::Rc<()> as Foo>, polarity:Positive), []), depth=0),Unimplemented), body_id=Some(BodyId { hir_id: HirId { owner: DefId(0:12 ~ issue_xxxxx_specialization[8170]::main), local_id: 12 } }), fallback_has_occurred=false

If you take a look at Example 4 in #90601, you can see that this can result in detrimentally unhelpful error messages in more complex cases.

cc #13231 #31844 #68970

@rustbot label +A-specialization +A-traits +D-confusing +D-terse +F-auto_traits +F-specialization

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-specializationArea: Trait impl specializationA-trait-systemArea: Trait systemD-confusingDiagnostics: Confusing error or lint that should be reworked.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.F-rustc_attrsInternal rustc attributes gated on the `#[rustc_attrs]` feature gate.F-specialization`#![feature(specialization)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.requires-nightlyThis issue requires a nightly compiler in some way.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions