Skip to content

relating projection substs is unsound during coherence #102048

Description

@lcnr
// This must fail coherence.
//
// Getting this to pass was fairly difficult, so here's an explanation
// of what's happening:
//
// Normalizing projections currently tries to replace them with inference variables
// while emitting a nested `Projection` obligation. This cannot be done if the projection
// has bound variables which is the case here.
//
// So the projections stay until after normalization. When unifying two projections we
// currently treat them as if they are injective, so we **incorrectly** unify their
// substs. This means that coherence for the two impls ends up unifying `?T` and `?U`
// as it tries to unify `<?T as WithAssoc1<'a>>::Assoc` with `<?U as WithAssoc1<'a>>::Assoc`.
//
// `impl1` therefore has the projection `<?T as WithAssoc2<'a>>::Assoc` and we have the
// assumption `?T: for<'a> WithAssoc2<'a, Assoc = i32>` in the `param_env`, so we normalize
// that to `i32`. We then try to unify `i32` from `impl1` with `u32` from `impl2` which fails,
// causing coherence to consider these two impls distinct.
pub trait Trait<T> {}

pub trait WithAssoc1<'a> {
    type Assoc;
}
pub trait WithAssoc2<'a> {
    type Assoc;
}

// impl 1
impl<T, U> Trait<for<'a> fn(<T as WithAssoc1<'a>>::Assoc, <U as WithAssoc2<'a>>::Assoc)> for (T, U)
where
    T: for<'a> WithAssoc1<'a> + for<'a> WithAssoc2<'a, Assoc = i32>,
    U: for<'a> WithAssoc2<'a>,
{
}

// impl 2
impl<T, U> Trait<for<'a> fn(<U as WithAssoc1<'a>>::Assoc, u32)> for (T, U) where
    U: for<'a> WithAssoc1<'a>
{
}

this currently compiles even though the following crate would mean that both impls overlap:

extern crate test1;
use test1::*;

struct Ty1;
struct Ty2;

impl WithAssoc1<'_> for Ty1 {
    type Assoc = ();
}
impl WithAssoc2<'_> for Ty1 {
    type Assoc = i32;
}
impl WithAssoc1<'_> for Ty2 {
    type Assoc = ();
}
impl WithAssoc2<'_> for Ty2 {
    type Assoc = u32;
}

fn foo<T, U>()
where
    (T, U): Trait<for<'a> fn((), u32)>,
{
}

fn main() {
    foo::<Ty1, Ty2>();
}

however, it seems like rustc generally has issues with both of these impls, so it actually believes that none of these impls apply 😁 see also #102047 where the first impl results in an ICE.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityT-typesRelevant to the types team, which will review and decide on the PR/issue.fixed-by-next-solverFixed by the next-generation trait solver, `-Znext-solver`.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions