Closed lcnr closed 1 month ago
Tracked in https://github.com/rust-lang/trait-system-refactor-initiative/issues/101 instead
it actually is possible to get overlapping impls from this. The following results in an ICE:
struct W<T: ?Sized>(*const T);
trait Trait<T: ?Sized> {
type Assoc;
}
// This would trigger the check for overlap between automatic and custom impl.
// They actually don't overlap so an impl like this should remain possible
// forever.
//
// impl Trait<u64> for dyn Trait<u32> {}
trait Indirect {}
impl Indirect for dyn Trait<u32, Assoc = ()> {}
impl<T: Indirect + ?Sized> Trait<u64> for T {
type Assoc = ();
}
trait Overlap<U: ?Sized> {
type Assoc: Default;
}
impl<T: ?Sized + EvaluateHack<W<U>>, U: ?Sized> Overlap<U> for T {
type Assoc = Box<u32>;
}
impl<U: ?Sized> Overlap<U> for dyn Trait<u32, Assoc = ()> {
type Assoc = usize;
}
// Incomplete impl where `dyn Trait<u32>: Trait<_>` does not hold, but
// `dyn Trait<u32>: Trait<u64>` does.
trait EvaluateHack<U: ?Sized> {}
impl<T: ?Sized, U: ?Sized> EvaluateHack<W<U>> for T
where
T: Trait<U, Assoc = ()>, // incompletely constrains `_` to `u32`
U: IsU64,
T: Trait<U, Assoc = ()>, // incompletely constrains `_` to `u32`
{
}
trait IsU64 {}
impl IsU64 for u64 {}
fn overlap<T: Overlap<U> + ?Sized, U>() -> T::Assoc {
Default::default()
}
fn generic_first<T: ?Sized + EvaluateHack<W<U>>, U>() {
overlap::<T, U>();
}
fn main() {
generic_first::<dyn Trait<u32, Assoc = ()>, u64>();
}
https://github.com/rust-lang/rust/blob/9b8dbd558c1c4b25c55d987e22baba312ae980ad/compiler/rustc_trait_selection/src/traits/project.rs#L1255-L1262
This ignores impl candidates even if the builtin object candidate guides inference, which is incomplete. Incompleteness during coherence is unsound, outside of coherence it can merely result in bad and confusing errors.
currently results in
which is also buggy :grin: adding that bound to the impl makes it compile but now they actually don't overlap because using the first impl would result in an inductive cycle. In the future all traits will be coinductive at which point this would be unsound. But as that will only happen in the new solver, we're safe.
It does however guide inference in incorrect ways, causing an additional - hopefully merely theoretical - breaking change with the new solver:
incorrect inference results in the following error in
main
: