rust-lang / rust-lang/rust

impl for dyn Trait + deref to dyn Trait + Marker unhelpful messages

Open
#118,875 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics A-trait-system D-terse T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
use std::error::Error;

trait ErrorReport {
    fn report(&self);
}
impl<E: Error + Sized> ErrorReport for E {
    fn report(&self) { /* real impl does something requiring Self: Sized */ }
}
impl ErrorReport for dyn Error /* missing marker traits  + Send + Sync */ {
    fn report(&self) {}
}

trait ErrorReportWithoutBlanket {
    fn report_without_blanket(&self);
}
impl ErrorReportWithoutBlanket for dyn Error /* missing marker traits  + Send + Sync */ {
    fn report_without_blanket(&self) {}
}

#[derive(Debug, thiserror::Error)]
#[error("my error")]
struct MyError;

fn main() {
    MyError.report();
    let ae: anyhow::Error = MyError.into();
    
    // >  error[E0599]: the method `report` exists for trait object `dyn Error + Send + Sync`,
    // >                but its trait bounds were not satisfied
    // this message ^ if you read it carefully does highlight the problem,
    // namely that dyn Error != dyn Error + Send + Sync
    (*ae).report();
    (*ae).report_without_blanket();
    
    // >  error[E0599]: the method `report` exists for struct `Error`,
    // >                but its trait bounds were not satisfied
    // the programmer is hoping to get the method via the deref so this isn't relevant
    //
    // >   ::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/error.rs:32:1
    // >     |
    // > 32  | pub trait Error: Debug + Display {
    // >     | --------------------------------
    // >     | |
    // >     | doesn't satisfy `dyn std::error::Error + Send + Sync: ErrorReport`
    // >     | doesn't satisfy `dyn std::error::Error + Send + Sync: Sized`
    // this is a complaint that the blanket impl isn't relevant:
    // as the programmer can see from the complaint about Sized
    //
    // >   ::: /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/anyhow-1.0.75/src/lib.rs:374:1
    // >     |
    // > 374 | pub struct Error {
    // >     | ----------------
    // >     | |
    // >     | doesn't satisfy `anyhow::Error: ErrorReport`
    // >     | doesn't satisfy `anyhow::Error: std::error::Error`
    // >     |
    // > note: the following trait bounds were not satisfied:
    // >       `anyhow::Error: std::error::Error`
    // >       `dyn std::error::Error + Send + Sync: Sized`
    // >    --> src/main.rs:6:6
    // >     |
    // > 6   | impl<E: Error + Sized> ErrorReport for E {
    // >     |      ^  ^^^^^   ^^^^^  -----------     -
    // >     |      |  |       |
    // >     |      |  |       unsatisfied trait bound introduced here
    // >     |      |  unsatisfied trait bound introduced here
    // >     |      unsatisfied trait bound introduced here
    // > help: consider relaxing the type parameter's implicit `Sized` bound
    // >     |
    // > 6   | impl<E: ?Sized + Error + Sized> ErrorReport for E {
    // >     |         ++++++++
    // this is an essay about the same thing.
    //
    // Note there is no message explaining why the impl for dyn Error isn't relevant.
    ae.report();

    // > error[E0599]: no method named `report_without_blanket` found for struct `anyhow::Error` in the current scope
    // >   --> src/main.rs:78:8
    // >    |
    // > 78 |     ae.report_without_blanket();
    // >    |        ^^^^^^^^^^^^^^^^^^^^^^ method not found in `Error`
    // >    |
    // >    = help: items from traits can only be used if the trait is implemented and in scope
    // > note: `ErrorReportWithoutBlanket` defines an item `report_without_blanket`, perhaps you need to implement it
    // >   --> src/main.rs:13:1
    // >    |
    // > 13 | trait ErrorReportWithoutBlanket {
    // >    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //
    // No mention of the impl for the Deref.
    ae.report_without_blanket();
}
Current output
error[E0599]: the method `report` exists for trait object `dyn Error + Send + Sync`, but its trait bounds were not satisfied
  --> src/main.rs:32:11
   |
32 |     (*ae).report();
   |           ^^^^^^ method cannot be called on `dyn Error + Send + Sync` due to unsatisfied trait bounds
   |
  ::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/error.rs:32:1
   |
32 | pub trait Error: Debug + Display {
   | --------------------------------
   | |
   | doesn't satisfy `dyn std::error::Error + Send + Sync: ErrorReport`
   | doesn't satisfy `dyn std::error::Error + Send + Sync: Sized`
   |
note: trait bound `dyn std::error::Error + Send + Sync: Sized` was not satisfied
  --> src/main.rs:6:6
   |
6  | impl<E: Error + Sized> ErrorReport for E {
   |      ^          ^^^^^  -----------     -
   |      |          |
   |      |          unsatisfied trait bound introduced here
   |      unsatisfied trait bound introduced here
help: consider relaxing the type parameter's implicit `Sized` bound
   |
6  | impl<E: ?Sized + Error + Sized> ErrorReport for E {
   |         ++++++++

error[E0599]: no method named `report_without_blanket` found for trait object `(dyn std::error::Error + Send + Sync + 'static)` in the current scope
  --> src/main.rs:33:11
   |
33 |     (*ae).report_without_blanket();
   |           ^^^^^^^^^^^^^^^^^^^^^^ method not found in `dyn Error + Send + Sync`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
note: `ErrorReportWithoutBlanket` defines an item `report_without_blanket`, perhaps you need to implement it
  --> src/main.rs:13:1
   |
13 | trait ErrorReportWithoutBlanket {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0599]: the method `report` exists for struct `Error`, but its trait bounds were not satisfied
   --> src/main.rs:75:8
    |
75  |     ae.report();
    |        ^^^^^^ method cannot be called on `Error` due to unsatisfied trait bounds
    |
   ::: /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/error.rs:32:1
    |
32  | pub trait Error: Debug + Display {
    | --------------------------------
    | |
    | doesn't satisfy `dyn std::error::Error + Send + Sync: ErrorReport`
    | doesn't satisfy `dyn std::error::Error + Send + Sync: Sized`
    |
   ::: /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/anyhow-1.0.75/src/lib.rs:374:1
    |
374 | pub struct Error {
    | ----------------
    | |
    | doesn't satisfy `anyhow::Error: ErrorReport`
    | doesn't satisfy `anyhow::Error: std::error::Error`
    |
note: the following trait bounds were not satisfied:
      `anyhow::Error: std::error::Error`
      `dyn std::error::Error + Send + Sync: Sized`
   --> src/main.rs:6:6
    |
6   | impl<E: Error + Sized> ErrorReport for E {
    |      ^  ^^^^^   ^^^^^  -----------     -
    |      |  |       |
    |      |  |       unsatisfied trait bound introduced here
    |      |  unsatisfied trait bound introduced here
    |      unsatisfied trait bound introduced here
help: consider relaxing the type parameter's implicit `Sized` bound
    |
6   | impl<E: ?Sized + Error + Sized> ErrorReport for E {
    |         ++++++++

error[E0599]: no method named `report_without_blanket` found for struct `anyhow::Error` in the current scope
  --> src/main.rs:91:8
   |
91 |     ae.report_without_blanket();
   |        ^^^^^^^^^^^^^^^^^^^^^^ method not found in `Error`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
note: `ErrorReportWithoutBlanket` defines an item `report_without_blanket`, perhaps you need to implement it
  --> src/main.rs:13:1
   |
13 | trait ErrorReportWithoutBlanket {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Desired output

Additional output should ideally say something like:

help: ErrorReport is implemented for dyn Error
      whereas anyhow::Error dereferences to dyn Error + Send + Sync
      which is not the same type.  Perhaps you should change the trait implementation
      or dereference implementation so that the trait bounds match.
Rationale and extra context

Implementing a trait for dyn Trait is a common workaround for various missing affordances. When this happens in combination with expectations of Deref, it would be nice if the compiler would explain the "near miss".

Other cases

This near miss logic should probably be applied sparingly to avoid generating a lot of noise from deref chains. If the miss is purely due to Send/Sync bounds discrepancies, it is probably very relevant.

Anything else?

Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e392d92f7912e5b7db2ff16ee580601a

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reproducer in src/main.rs and compare the current diagnostics for report and report_without_blanket with the desired near-miss explanation. Trace how method lookup handles the Deref from anyhow::Error to dyn Error + Send + Sync, and consider the stated Send/Sync mismatch case; done means the diagnostic clearly identifies the implemented dyn Error type and the dereferenced type without excessive deref-chain noise.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.