Skip to content

defaulted traits ought to be have more restrictive coherence rules #22978

Closed
@nikomatsakis

Description

@nikomatsakis

We initially had some rules that prevented explicit impls for defaulted traits for anything except for nominal types. In other words, if (in crate A) I have:

trait Foo { }
impl Foo for .. { }

then it was illegal to do (in any crate) struct SomeType { } impl Foo for (SomeType,) { }. However, that seemed too strict, because one wants to be able to do things like impl !Foo for *mut A { }, and since (currently) positive and negative impls basically follow the same rules, it seemed strange to permit one and not the other. So we removed this logic.

But we got it wrong. The current rules do not guarantee coherence well enough to avoid inconsistent conclusions. In particular, I can define in crate A a generic fn like:

fn test<X:Foo,Y:Foo>() {
    // in here, we can conclude that (X,Y) : Foo
}

This function can conclude that (X,Y) : Foo because it applies the default rule for the tuple and the components implement Foo.

But now in crate B I can do:

struct A { }
struct B { }
impl Foo for A { }
impl Foo for B { }
impl !Send for (A, B) { }

In other words, A and B are Foo, but not the pair (A,B). Uh oh!

I think what the rules ought to be is that, outside the crate where the trait was defined, we can only define an impl for a defaulted trait if the self-type is a ty_struct or ty_enum and the type itself was defined in the current crate.

cc @flaper87

Metadata

Metadata

Assignees

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