rust-lang / rust-lang/rust

E0277: Unhelpful error message is given when indirect constraints cause blanket implementations to not get implemented

Open
#134,346 1 comment 6 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Code
// The following code demonstrates an incomplete error message returned from
// the Rust compiler, when there are unsatisfied constraints present in
// code that makes use of _context-generic programming_, as described at
// https://patterns.contextgeneric.dev/.
//
// More details about similar error is described here:
// https://patterns.contextgeneric.dev/debugging-techniques.html


// The trait to link a consumer trait with a provide trait, as explained by:
// https://patterns.contextgeneric.dev/consumer-provider-link.html#blanket-consumer-trait-implementation
pub trait HasComponents {
    type Components;
}

// The trait to delegate a provider implementation to another provider, as explained by:
// https://patterns.contextgeneric.dev/provider-delegation.html#blanket-provider-implementation
pub trait DelegateComponent<Name> {
    type Delegate;
}

// The name of out example component
pub struct StringFormatterComponent;

// Our example consumer trait, with the context type being the implicit `Self` type
pub trait CanFormatToString {
    fn format_to_string(&self) -> String;
}

// Our example provider trait, with the context type being the explicit `Context` type
pub trait StringFormatter<Context> {
    fn format_to_string(context: &Context) -> String;
}

// A blanket implementation that links the consumer `CanFormatToString` with
// the provider `StringFormatter`, using `HasComponents`
impl<Context> CanFormatToString for Context
where
    Context: HasComponents,
    Context::Components: StringFormatter<Context>,
{
    fn format_to_string(&self) -> String {
        Context::Components::format_to_string(self)
    }
}

// A blanket implementation that links the provider implementation of
// `StringFormatter` with another provider, using `DelegateComponent`.
impl<Component, Context> StringFormatter<Context> for Component
where
    Component: DelegateComponent<StringFormatterComponent>,
    Component::Delegate: StringFormatter<Context>,
{
    fn format_to_string(context: &Context) -> String {
        Component::Delegate::format_to_string(context)
    }
}

// An example provider for `StringFormatter`, which has a generic
// implementation that requires `Context: debug`
pub struct FormatWithDebug;

impl<Context> StringFormatter<Context> for FormatWithDebug
where
    Context: core::fmt::Debug,
{
    fn format_to_string(context: &Context) -> String {
        format!("{:?}", context)
    }
}

// An example concrete context.
// Note: we pretend to forgot to derive `Debug` to cause error to be raised.
// FIXME: Uncomment the line below to fix the error.
// #[derive(Debug)]
pub struct Person {
    pub first_name: String,
    pub last_name: String,
}

// The components tied to the `Person` context
pub struct PersonComponents;

// Implement the consumer traits for `Person` using
// the aggregated provider `PersonComponents`
impl HasComponents for Person {
    type Components = PersonComponents;
}

// Implement `PersonComponents: StringFormatter<Person>` using `FormatWithDebug`
impl DelegateComponent<StringFormatterComponent> for PersonComponents {
    type Delegate = FormatWithDebug;
}

// Checks that `Person` implements `CanFormatToString`
pub trait CanUsePerson: CanFormatToString {}

// This should raise an error, since we didn't implement `Debug` for `Person`
impl CanUsePerson for Person {}
Current output
error[E0277]: the trait bound `FormatWithDebug: StringFormatter<Person>` is not satisfied
  --> lib.rs:99:23
   |
99 | impl CanUsePerson for Person {}
   |                       ^^^^^^ the trait `StringFormatter<Person>` is not implemented for `FormatWithDebug`, which is required by `Person: CanFormatToString`
   |
   = help: the trait `StringFormatter<Context>` is implemented for `FormatWithDebug`
note: required for `PersonComponents` to implement `StringFormatter<Person>`
  --> lib.rs:49:26
   |
49 | impl<Component, Context> StringFormatter<Context> for Component
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^
...
52 |     Component::Delegate: StringFormatter<Context>,
   |                          ------------------------ unsatisfied trait bound introduced here
note: required for `Person` to implement `CanFormatToString`
  --> lib.rs:37:15
   |
37 | impl<Context> CanFormatToString for Context
   |               ^^^^^^^^^^^^^^^^^     ^^^^^^^
...
40 |     Context::Components: StringFormatter<Context>,
   |                          ------------------------ unsatisfied trait bound introduced here
note: required by a bound in `CanUsePerson`
  --> lib.rs:96:25
   |
96 | pub trait CanUsePerson: CanFormatToString {}
   |                         ^^^^^^^^^^^^^^^^^ required by this bound in `CanUsePerson`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Desired output
error[E0277]: the trait bound `FormatWithDebug: StringFormatter<Person>` is not satisfied
  --> lib.rs:99:23
   |
99 | impl CanUsePerson for Person {}
   |                       ^^^^^^ the trait `StringFormatter<Person>` is not implemented for `FormatWithDebug`
   |
   = help: the following constraint is not satisfied: `Person: Debug`
   = help: the trait `StringFormatter<Context>` is implemented for `FormatWithDebug`
note: required for `PersonComponents` to implement `StringFormatter<Person>`
  --> lib.rs:49:26
   |
49 | impl<Component, Context> StringFormatter<Context> for Component
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^
...
52 |     Component::Delegate: StringFormatter<Context>,
   |                          ------------------------ unsatisfied trait bound introduced here
note: required for `Person` to implement `CanFormatToString`
  --> lib.rs:37:15
   |
37 | impl<Context> CanFormatToString for Context
   |               ^^^^^^^^^^^^^^^^^     ^^^^^^^
...
40 |     Context::Components: StringFormatter<Context>,
   |                          ------------------------ unsatisfied trait bound introduced here
note: required by a bound in `CanUsePerson`
  --> lib.rs:96:25
   |
96 | pub trait CanUsePerson: CanFormatToString {}
   |                         ^^^^^^^^^^^^^^^^^ required by this bound in `CanUsePerson`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Rationale and extra context

Summary

When resolving Rust constraints that involve blanket implementations, Rust does not produce helpful error messages when missing indirect constraints caused the blanket implementation to not get implemented.

Rationale

The lack of informative error messages presents challenges in a new project that I am working on, context-generic programming, which implements a modular component system for Rust by making extensive use of blanket implementations. More details about how this error arised is described in the chapter of my book for debugging techniques.

For the purpose of this issue, I have attached a minimal code snippet with desugared code, so that it can be tested without importing my library cgp. For simplicity, the example code may look silly and does not demonstrate why it is written that way. The main purpose of the example code is to reproduce an example error message with as little code as possible.

Other cases

Rust Version
rustc 1.83.0 (90b35a623 2024-11-26)
binary: rustc
commit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf
commit-date: 2024-11-26
host: aarch64-unknown-linux-gnu
release: 1.83.0
LLVM version: 19.1.1
Anything else?

The example code is also available on Rust Playground.

I already have a fix for this issue, and it is now available at #134348.

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 minimal Rust example in lib.rs, also available on Rust Playground, and reproduce the E0277 diagnostic with rustc 1.83.0. Compare it with the desired output, then read issue #134348; done means the indirect Person: Debug constraint is reported without the misleading blanket-implementation wording.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.