Skip to content

Trait impls from where clauses (ParamEnv) take precedence over freestanding trait impls #24066

@hanna-kruppe

Description

@hanna-kruppe

MVCE:

trait Trait<T> {
    fn call_me(&self, x: T) {}
}
impl<T> Trait<u32> for T {}
impl<T> Trait<i32> for T {}
fn bug<T: Trait<U>, U>(x: T) {
    x.call_me(1u32);
    //~^ ERROR mismatched types
}

This is due to the fact that we prefer where-bounds over impls, even if the where-bounds have unnecessary inference constraints.


I stumbled upon this trying to write a function that generically takes a range (or something else with which str can be indexed), and slices a str with that range. I got that to work, this bug comes into play when the str is produced by slicing another string with a RangeFrom or another concrete type (in the same function).

Here's a simpler (no lifetimes, no associated types) and self-contained example (playpen):

trait Frobnicate<I> {
    fn frob(&self, i: I) -> Self;
}

struct Thing;

struct IA;
struct IB;

impl Frobnicate<IA> for Thing {
    fn frob(&self, _i: IA) -> Thing {
        Thing
    }
}

impl Frobnicate<IB> for Thing {
    fn frob(&self, _i: IB) -> Thing {
        Thing
    }
}

fn delegate_frob<I>(t: Thing, i: I) -> Thing
where
    Thing: Frobnicate<I>,
{
    let t2: Thing = t.frob(IA);
    t2.frob(i)
}

This seems innocent enough. There's a impl Frobnicate<IA> for Thing, so the .frob(IA) call should work, and it's known to return Thing again, so via the where clause the .frob(i) call is good as well. However, I get this error message:

error[E0308]: mismatched types
  --> src/main.rs:26:28
   |
22 | fn delegate_frob<I>(t: Thing, i: I) -> Thing
   |                  - expected this type parameter
...
26 |     let t2: Thing = t.frob(IA);
   |                       ---- ^^ expected type parameter `I`, found `IA`
   |                       |
   |                       arguments to this method are incorrect
   |
   = note: expected type parameter `I`
                      found struct `IA`
note: method defined here
  --> src/main.rs:2:8
   |
2  |     fn frob(&self, i: I) -> Self;
   |        ^^^^        -

It appears that the where clause makes the compiler forget that there are other impls.
Adding a Thing : Frobnicate<IA> bound only makes the compiler (rightly) complain that that's not a bound at all, since it doesn't involve any type parameters.
UFCS makes it compile, though:

    let t2: Thing = <Thing as Frobnicate<IA>>::frob(&t, IA);

Edit: Besides playpen, I can also reproduce this locally:

rustc 1.0.0-beta (9854143cb 2015-04-02) (built 2015-04-02)
binary: rustc
commit-hash: 9854143cba679834bc4ef932858cd5303f015a0e
commit-date: 2015-04-02
build-date: 2015-04-02
host: x86_64-pc-windows-gnu
release: 1.0.0-beta

But I already noticed it a couple of weeks ago, so it can't be a recent regression.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-trait-systemArea: Trait systemC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types 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