Description
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:
- subst-relate: fails because we can't directly equate the substs of different alias kinds.
- normalizes-to-rhs:
Tait normalizes-to <[i32; 32] as IntoIterator>::IntoIter
- Ends up infering opaque definition -
Tait := <[i32; 32] as IntoIterator>::IntoIter
- Ends up infering opaque definition -
- 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>
andTait
- Ends up infering opaque definition -
Tait := std::array::IntoIter<i32, 32>
- Ends up infering opaque definition -
- Find impl candidate, substitute the associated type -
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.