Skip to content

Specializations and negative traits are resolved in a wrong way. #66041

@wdanilo

Description

@wdanilo

Hi, consider the following code:

#![feature(specialization)]
#![feature(optin_builtin_traits)]

use std::ops::Deref;
use std::ops::DerefMut;

// =================
// === WithLabel ===
// =================

struct WithLabel<T>(String, T);

auto trait IsNotWithLabel {}
impl<T> !IsNotWithLabel for WithLabel<T> {}

pub trait HasLabel {
    fn label(&self) -> &String;
}

impl<T> HasLabel for WithLabel<T> {
    fn label(&self) -> &String { 
        &self.0
    }
}

impl<T> HasLabel for T
where T: Deref + IsNotWithLabel, <Self as Deref>::Target : HasLabel {
    default fn label(&self) -> &String { 
        self.deref().label() 
    }
}

impl<T> Deref for WithLabel<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.1
    }
}

// ==============
// === WithID ===
// ==============

struct WithID<T>(i32, T);

pub trait HasID {
    fn id(&self) -> &i32;
}

impl<T> HasID for WithID<T> {
    fn id(&self) -> &i32 { 
        &self.0
    }
}

auto trait IsNotWithID {}
impl<T> !IsNotWithID for WithID<T> {}

impl<T> HasID for T
where T: Deref + IsNotWithID, <Self as Deref>::Target : HasID {
    default fn id(&self) -> &i32 { 
        self.deref().id() 
    }
}

impl<T> Deref for WithID<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.1
    }
}

// =============
// === Usage ===
// =============

struct A(i32);

type X = WithLabel<WithID<A>>;

fn test<T: HasID + HasLabel> (t: T) {
    println!("{:?}", t.label());
    println!("{:?}", t.id());
}

fn main() {
    let v1 = WithLabel("label1".to_string(), WithID(0, A(1)));
    // test(v1); // THIS DOES NOT COMPILE
}

Everything compiles fine, but the last line (commented out). The error is:

error[E0277]: the trait bound `WithID<A>: IsNotWithID` is not satisfied in `WithLabel<WithID<A>>`
  --> src/main.rs:88:10
   |
81 | fn test<T: HasID + HasLabel> (t: T) {
   |    ----    ----- required by this bound in `test`
...
88 |     test(v1);
   |          ^^ within `WithLabel<WithID<A>>`, the trait `IsNotWithID` is not implemented for `WithID<A>`
   |
   = help: the following implementations were found:
             <WithID<T> as IsNotWithID>
   = note: required because it appears within the type `WithLabel<WithID<A>>`
   = note: required because of the requirements on the impl of `HasID` for `WithLabel<WithID<A>>`

And this is not correct. The type of v1 is WithLabel<WithID<A>>. When passing it to test we must prove that WithLabel<WithID<A>> : HasID + HasLabel. Let's start with HasID:

  • The impl<T> HasID for WithID<T> is obviously not matched.
  • The impl<T> HasID for T is matched, including it's constraints: T: Deref + IsNotWithID, and <Self as Deref>::Target : HasID, because Target resolves to WithID<A> and there is only 1 instance that matches WithID<A>: HasID.

The constraint WithLabel<WithID<A>> : HasLabel is simple to be checked – only the impl<T> HasLabel for WithLabel<T> is matched here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions