-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Regression on Tuple.Union when using Tuple.toList #16583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Strangely enough, val ll0a = ll0.toList is the only example that fails on both versions. |
Minimized to def test(tup: ("one", "two", "three")) = tup.toList
|
Bisecting the minimized version I found that the first bad release was |
The original example started failing in |
46e3d77 can cause this only by fixing a bug and therefore uncovering the underltying cause. I am afraid if we want to fix this we'll have to untangle the underlying matchtype resolution. I won't be able to do this; someone else will need to step up. |
Adding a /** True if `tp1` and `tp2` have compatible type constructors and their
* corresponding arguments are subtypes relative to their variance (see `isSubArgs`).
*/
- def isMatchingApply(tp1: Type): Boolean = tp1.widen match {
+ def isMatchingApply(tp1: Type): Boolean = tp1.simplified match {
case tp1 @ AppliedType(tycon1, args1) =>
// We intentionally do not automatically dealias `tycon1` or `tycon2` here.
// `TypeApplications#appliedTo` already takes care of dealiasing type But I guess |
Yes, simplified works on a much higher level. But it's maybe a good angle to explore further. What are the types involved where simplified works but widen doesn't? |
I think the problem is when both Problematic ==> isSubType List[Tuple.Union[(tup : (("one" : String), ("two" : String), ("three" : String)))]] <:< List[("one" : String) | (("two" : String) | (("three" : String) | Nothing))]?
==> isSubType Tuple.Union[(tup : (("one" : String), ("two" : String), ("three" : String)))] <:< ("one" : String) | (("two" : String) | (("three" : String) | Nothing))?
==> isSubType Tuple.Union[(tup : (("one" : String), ("two" : String), ("three" : String)))] <:< Nothing Maybe I also tried: def isMatchingApply(tp1: Type): Boolean = tp1.widen match {
+ case MatchType.InDisguise(mt) => isMatchingApply(mt.simplified)
+ case mt: MatchType => isMatchingApply(mt.simplified)
case tp1 @ AppliedType(tycon1, args1) =>
// We intentionally do not automatically dealias `tycon1` or `tycon2` here.
// `TypeApplications#appliedTo` already takes care of dealiasing type But this is not enough. I'll further investigate. |
Yes, if one of the compared types is a MatchType we should try to normalize it (which means reducing it). I am puzzled why this is not already the case. |
Mmh, that's the problem: tp2.atoms match
case Atoms.Range(lo2, hi2) if canCompareAtoms && canCompare(hi2) =>
tp1.atoms match
case Atoms.Range(lo1, hi1) =>
if hi1.subsetOf(lo2) || knownSingletons && hi2.size == 1 && hi1 == hi2 then
Some(verified(true))
else if !lo1.subsetOf(hi2) then
Some(verified(false))
else
None
- case _ => Some(verified(recur(tp1, NothingType)))
+ case _ => None
case _ => None
|
Well spotted! Yes, that looks plausible. |
I've found that add this case to the definition of // this corresponds to the special case in TypeComparer
// which ensures that `X$ <:< X.type` returns true.
single(tp.symbol.companionModule.termRef.asSeenFrom(tp.prefix, tp.symbol.owner))
+ case tp: MatchType if tp.reduced.exists =>
+ tp.reduced.atoms
case tp: TypeProxy =>
tp.superType.atoms match
case Atoms.Range(_, hi) => Atoms.Range(Set.empty, hi) But it doesn't fix the rest of the original....
|
Not the first time I've wanted isSubType to check both sides for an operation before going to the next case (I ran into this in #16744). |
Maybe: diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
index f097bd160fd..8d99a2d2d97 100644
--- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -1578,7 +1578,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
Some(verified(false))
else
None
- case _ => Some(verified(recur(tp1, NothingType)))
+ case _ if knownSingletons =>
+ Some(verified(recur(tp1, NothingType)))
+ case _ =>
+ None
case _ => None ? Generally, computing atoms on types that could be further reduced/normalized seems fishy to me. |
In case of original It still has the same issue, I believe
|
Looking with @Decel; |
Now with the first part fixed, the issue is the same as #16654 |
Thinking about it again, I wonder if this doesn't boil down to invariance of match types not playing well with inferred singleton types. This does not hold: type Labels <: Tuple
val x: Labels = ???
val v3: Tuple.Union[Labels] = (???): Tuple.Union[x.type]
// Found: Tuple.Union[(x : Labels)]
// Required: Tuple.Union[Labels] Consequently, this also can't hold: val v2: List[Tuple.Union[Labels]] = (???): List[Tuple.Union[x.type]] Nor: val v1: List[
Labels match
case EmptyTuple => Nothing
case h *: t => h | Tuple.Fold[t, Nothing, [x, y] =>> x | y]
] = (???): List[Tuple.Union[x.type]] So I guess the question is more: why is the skolem |
Further minimized: def toList(x: Tuple): List[Tuple.Union[x.type]] = ???
def test[Labels <: Tuple] = toList((???): Labels)
// Found: List[Tuple.Union[(?1 : Labels)]]
// Required: List[
// Labels match {
// case EmptyTuple => Nothing
// case h *: t => h | scala.Tuple.Fold[t, Nothing, [x, y] =>> x | y]
// }
// ] And without match types or tuples: type F2[X, Y]
type F[X] = F2[X, X]
def toF(x: Any): F[x.type] = ???
def test[T] = toF((???): T)
// def test[T] = toF((???): T)
// ^^^^^^^^^^^^^
// Found: F[(?1 : T)]
// Required: F2[T, T]
// where: ?1 is an unknown value of type T Type-avoidance related? |
In some of these cases, the skolem disappears because it is first converted to a And then approximated to its higher bound in
|
Here is a self-contained example of val Id = HKTypeLambda(List(T), List(Invariant))(
_ => List(TypeBounds.empty),
self => self.paramRefs(0)
)
assertTypesEquiv(
Id.appliedTo(TypeBounds(defn.NothingType, defn.IntType)),
defn.IntType
) // Pass |
@mbovel That's because the truth for a HKTypeLambda is its structure, not the way it is declared. Declarations are only needed for named types. I.e. type Foo[T] = [X] => X Here, type I added an explanation of this in #17554. |
Compiler version
Worked on
3.2.1
Fails on
3.2.2-RC2
Minimized code
Output
Expectation
That it will compile on the latest Scala compiler version.
The text was updated successfully, but these errors were encountered: