Skip to content

Poor diagnostics for associated type mismatch on inherent method. #86377

Open
@jonhoo

Description

@jonhoo

Given the following code (playground):

// These are simplifications of the tower traits by the same name:

pub trait Service<Request> {
    type Response;
}

pub trait Layer<C> {
    type Service;
}

// Any type will do here:

pub struct Req;
pub struct Res;

// This is encoding a trait alias.

pub trait ParticularService:
    Service<Req, Response = Res> {
}

impl<T> ParticularService for T
where
    T: Service<Req, Response = Res>,
{
}

// This is also a trait alias.
// The weird = <Self as ...> bound is there so that users of the trait do not
// need to repeat the bounds. See https://github.com/rust-lang/rust/issues/20671
// for context, and in particular the workaround in:
// https://github.com/rust-lang/rust/issues/20671#issuecomment-529752828

pub trait ParticularServiceLayer<C>: Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service> {
    type Service: ParticularService;
}

impl<T, C> ParticularServiceLayer<C> for T
where
    T: Layer<C>,
    T::Service: ParticularService,
{
    type Service = T::Service;
}

// These are types that implement the traits that the trait aliases refer to.
// They should also implement the alias traits due to the blanket impls.

struct ALayer<C>(C);
impl<C> Layer<C> for ALayer<C> {
    type Service = AService;
}

struct AService;
impl Service<Req> for AService {
    // However, AService does _not_ meet the blanket implementation,
    // since its Response type is bool, not Res as it should be.
    type Response = bool;
}

// This is a wrapper type around ALayer that uses the trait alias
// as a way to communicate the requirements of the provided types.
struct Client<C>(C);

// The method and the free-standing function below both have the same bounds.

impl<C> Client<C>
where
    ALayer<C>: ParticularServiceLayer<C>,
{
    fn check(&self) {}
}

fn check<C>(_: C) where ALayer<C>: ParticularServiceLayer<C> {}

// But, they give very different error messages.

#[allow(dead_code)]
fn test() {
    // This gives a very poor error message that does nothing to point the user
    // at the underlying cause of why the types involved do not meet the bounds.
    Client(()).check();
    
    // This gives a good(ish) error message that points the user at _why_ the
    // bound isn't met, and thus how they might fix it.
    check(());
}

The current output (for the indicated bad case) is:

error[E0599]: the method `check` exists for struct `Client<()>`, but its trait bounds were not satisfied
  --> src/lib.rs:82:16
   |
49 | struct ALayer<C>(C);
   | -------------------- doesn't satisfy `ALayer<()>: ParticularServiceLayer<()>`
...
63 | struct Client<C>(C);
   | -------------------- method `check` not found for this
...
82 |     Client(()).check();
   |                ^^^^^ method cannot be called on `Client<()>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `ALayer<()>: ParticularServiceLayer<()>`

Ideally the output should look like (the current nightly output for the indicated good case):

error[E0271]: type mismatch resolving `<AService as Service<Req>>::Response == Res`
  --> src/lib.rs:82:5
   |
69 |     ALayer<C>: ParticularServiceLayer<C>,
   |     ------------------------------------ required by this bound in `impl<C> Client<C>`
...
82 |     Client(()).check();
   |                ^^^^^^^ expected struct `Res`, found `bool`
   |
note: required because of the requirements on the impl of `ParticularService` for `AService`
  --> src/lib.rs:22:9
   |
22 | impl<T> ParticularService for T
   |         ^^^^^^^^^^^^^^^^^     ^
note: required because of the requirements on the impl of `ParticularServiceLayer<_>` for `ALayer<_>`
  --> src/lib.rs:38:12
   |
38 | impl<T, C> ParticularServiceLayer<C> for T
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^     ^

Actually pointing at the associated type and where the requirement that it be Res comes from would also be helpful to make it perfect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsC-enhancementCategory: An issue proposing an enhancement or a PR with one.T-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