rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.85k stars 12.51k forks source link

Inconsistent behavior of auto_traits+negative_impls across crates. #108995

Open booti386 opened 1 year ago

booti386 commented 1 year ago

I tried this code:

// Crate a

#![feature(auto_traits)]
#![feature(negative_impls)]

pub auto trait NotVoid {}
impl !NotVoid for () {}

pub trait A<I, O> {}

impl<T, I: NotVoid> A<I, ()> for T {}
impl<T, O: NotVoid> A<(), O> for T {} // OK

use b::ExternNotVoid;

pub trait B<I, O> {}

impl<T, I: ExternNotVoid> B<I, ()> for T {}
impl<T, O: ExternNotVoid> B<(), O> for T {} // Error
// Crate b

#![feature(auto_traits)]
#![feature(negative_impls)]

pub auto trait ExternNotVoid {}
impl !ExternNotVoid for () {}

I expected to see this happen: Identical behavior when NotVoid is declared in an external crate.

Instead, this happened:

error[E0119]: conflicting implementations of trait `B<(), ()>`
  --> a.rs:19:1
   |
18 | impl<T, I: ExternNotVoid> B<I, ()> for T {} // Error
   | ---------------------------------------- first implementation here
19 | impl<T, O: ExternNotVoid> B<(), O> for T {} // Error
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

error: aborting due to previous error

For more information about this error, try `rustc --explain E0119`.

An error is produced when NotVoid is declared in an external crate (ExternNotVoid).

Meta

rustc --version --verbose:

rustc 1.70.0-nightly (e3dfeeaa4 2023-03-07)
binary: rustc
commit-hash: e3dfeeaa45f117281b19773d67f3f253de65cee1
commit-date: 2023-03-07
host: x86_64-unknown-linux-gnu
release: 1.70.0-nightly
LLVM version: 15.0.7
workingjubilee commented 1 year ago

I believe this problem looks like

pub trait B<I, O> {}
impl<T, I: ExternNotVoid> B<I, ()> for T {} // Generic over B<I, ()>
impl<T, O: ExternNotVoid> B<(), O> for T {} // I is generic,
// and () is !ExternNotVoid, so it can't be in I
// but that isn't factored into checking the validity of the implementation

I'm not sure which is the "intended" behavior. But I believe this is a known issue? There are several issues regarding negative impls, coherence-checking, their interaction with auto-traits, and their soundness:

At a glance, none seem like an exact match, but it could just be my inability to perceive a pattern where one exists.

booti386 commented 1 year ago

What particularly surprises me is that it works when everything is in the same crate, but stops working when NotVoid is declared in another crate.

workingjubilee commented 1 year ago

A crate is allowed to bend the rules slightly for its own trait-and-type implementations (due to not being subject as directly to the "orphan rule"). I'm not sure this case of such a weakening is sound (or due to the orphan rule and related coherence checks, for that matter).