Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
soareschen opened this issue Dec 15, 2024 · 0 comments · May be fixed by #134348
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@soareschen
Copy link

soareschen commented Dec 15, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
1 participant