Skip to content

Incompleteness via bidirectional normalizes-to #25

Closed
@compiler-errors

Description

@compiler-errors

We assemble three candidates for alias-relate goals -- normalizes-to-lhs and normalizes-to-rhs goals (assembling a projection goal for each side of the relation if it's an alias), and a subst-relate goal where we structurally relate aliases if they're both aliases of the same def-id.

Sometimes we get into the case where the choice of normalizes-to branch in alias-relate are both valid, but we cannot make a choice of which one to take because they are different -- either returning equivalent but permuted region constraints, or equivalent opaque type definitions but differing modulo normalization.

a TAIT example

type Tait = impl Iterator<Item = impl Sized>;

fn mk<T>() -> T { todo!() }

fn a() {
    let x: Tait = mk();
    let mut array = mk();
    let mut z = IntoIterator::into_iter(array);
    z = x;
    array = [0i32; 32];
}

Which fails with:

error[E0284]: type annotations needed: cannot satisfy `Tait <: <[i32; 32] as IntoIterator>::IntoIter`
  --> <source>:11:9
   |
11 |     z = x;
   |         ^ cannot satisfy `Tait <: <[i32; 32] as IntoIterator>::IntoIter`

Consider this failing goal: AliasRelate(Tait, <[i32; 32] as IntoIterator>::IntoIter).

As mentioned above, we have three different candidates to satisfy this goal:

  1. subst-relate: fails because we can't directly equate the substs of different alias kinds.
  2. normalizes-to-rhs: Tait normalizes-to <[i32; 32] as IntoIterator>::IntoIter
    • Ends up infering opaque definition - Tait := <[i32; 32] as IntoIterator>::IntoIter
  3. normalizes-to-lhs: <[i32; 32] as IntoIterator>::IntoIter normalizes-to Tait
    • Find impl candidate, substitute the associated type - std::array::IntoIter<i32, 32>
    • Equate std::array::IntoIter<i32, 32> and Tait
      • Ends up infering opaque definition - Tait := std::array::IntoIter<i32, 32>

The problem here is that (2.) and (3.) are equally valid and also essentially equivalent, since we have aliases that normalize on both sides, but due to lazy norm, they end up inferring different opaque type definitions that are only equal after normalizing them further.

an RPIT example

This isn't limited to TAITs, and this can be adapted to RPITs too:

fn test() -> impl Iterator<Item = impl Sized> {
    IntoIterator::into_iter([0i32; 32])
}

Failing similarly with:

error[E0284]: type annotations needed: cannot satisfy `<[i32; 32] as IntoIterator>::IntoIter <: impl Iterator<Item = impl Sized>`
 --> <source>:2:5
  |
2 |     IntoIterator::into_iter([0i32; 32])
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<[i32; 32] as IntoIterator>::IntoIter <: impl Iterator<Item = impl Sized>`

Solution(s)

Fall back to the intersection of both normalizes-to goals

we can make progress by considering a fourth candidate where we compute both normalizes-to branches together and canonicalize that as a response. This is essentially the AND intersection of both normalizes-to branches. In an ideal world, we'd be returning something more like the OR intersection of both branches, but we have no way of representing that either for regions (maybe eventually) or opaques (don't see that happening ever).

An attempt to fix this is open in rust-lang/rust#112076.

Eagerly normalize opaque type responses

Most of the easily constructible examples of this issue happening are due to opaque type responses. We could instead eagerly normalize opaque types in query responses so that try_merge_responses are more likely to succeed due to being identical responses.

Only combine responses if they're equal "modulo opaque types", and combine those

Same as the first proposed solution, but only being applied when responses differ in their opaque types.

Thoughts

This incompleteness seems to be a consequence of lazy norm and the current opaque type inference strategy, but I guess I could see it being weakened to only apply when opaque type inference is involved to avoid its fallout. Since we currently prefer subst-relate candidates in alias-relate ambiguity [todo issue], it's particularly difficult to construct a case where we need this for projections.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-incompleteincorrectly return `NoSolution`, unsound during coherence

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions