Skip to content

Commit 86355ea

Browse files
committed
Fix #8129: Refine atoms computation
It makes a difference whether a type appears to the left or to the right of a <:< comparison. On the left, an abstract type upper bounded by some atoms can assume to have these atoms. But on the right, that's not the case.
1 parent 704d329 commit 86355ea

File tree

3 files changed

+28
-15
lines changed

3 files changed

+28
-15
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
439439
(tp1.widenSingletons ne tp1) &&
440440
recur(tp1.widenSingletons, tp2)
441441

442-
if (tp2.atoms.nonEmpty && canCompare(tp2.atoms))
443-
tp1.atoms.nonEmpty && tp1.atoms.subsetOf(tp2.atoms)
442+
if (tp2.atoms().nonEmpty && canCompare(tp2.atoms()))
443+
val atoms1 = tp1.atoms(widenOK = true)
444+
atoms1.nonEmpty && atoms1.subsetOf(tp2.atoms())
444445
else
445446
widenOK
446447
|| joinOK
@@ -620,8 +621,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
620621
}
621622
compareTypeLambda
622623
case OrType(tp21, tp22) =>
623-
if (tp2.atoms.nonEmpty && canCompare(tp2.atoms))
624-
return tp1.atoms.nonEmpty && tp1.atoms.subsetOf(tp2.atoms) || isSubType(tp1, NothingType)
624+
if (tp2.atoms().nonEmpty && canCompare(tp2.atoms()))
625+
val atoms1 = tp1.atoms(widenOK = true)
626+
return atoms1.nonEmpty && atoms1.subsetOf(tp2.atoms()) || isSubType(tp1, NothingType)
625627

626628
// The next clause handles a situation like the one encountered in i2745.scala.
627629
// We have:
@@ -1797,9 +1799,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17971799
else if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp2
17981800
else {
17991801
def mergedLub: Type = {
1800-
val atoms1 = tp1.atoms
1802+
val atoms1 = tp1.atoms(widenOK = true)
18011803
if (atoms1.nonEmpty && !widenInUnions) {
1802-
val atoms2 = tp2.atoms
1804+
val atoms2 = tp2.atoms(widenOK = true)
18031805
if (atoms2.nonEmpty) {
18041806
if (atoms1.subsetOf(atoms2)) return tp2
18051807
if (atoms2.subsetOf(atoms1)) return tp1

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,8 +1156,10 @@ object Types {
11561156
/** If this type is an alias of a disjunction of stable singleton types,
11571157
* these types as a set, otherwise the empty set.
11581158
* Overridden and cached in OrType.
1159+
* @param widenOK If type proxies that are upperbounded by types with atoms
1160+
* have the same atoms.
11591161
*/
1160-
def atoms(implicit ctx: Context): Set[Type] = dealias match {
1162+
def atoms(widenOK: Boolean = false)(implicit ctx: Context): Set[Type] = dealias match {
11611163
case tp: SingletonType =>
11621164
def normalize(tp: Type): Type = tp match {
11631165
case tp: SingletonType =>
@@ -1171,12 +1173,13 @@ object Types {
11711173
}
11721174
case _ => tp
11731175
}
1174-
val underlyingAtoms = tp.underlying.atoms
1176+
val underlyingAtoms = tp.underlying.atoms(widenOK)
11751177
if (underlyingAtoms.isEmpty && tp.isStable) Set.empty + normalize(tp)
11761178
else underlyingAtoms
1177-
case tp: ExprType => tp.resType.atoms
1178-
case tp: OrType => tp.atoms // `atoms` overridden in OrType
1179-
case tp: AndType => tp.tp1.atoms & tp.tp2.atoms
1179+
case tp: ExprType => tp.resType.atoms(widenOK)
1180+
case tp: OrType => tp.atoms(widenOK) // `atoms` overridden in OrType
1181+
case tp: AndType => tp.tp1.atoms(widenOK) & tp.tp2.atoms(widenOK)
1182+
case tp: TypeProxy if widenOK => tp.underlying.atoms(widenOK)
11801183
case _ => Set.empty
11811184
}
11821185

@@ -2894,22 +2897,26 @@ object Types {
28942897

28952898
private var atomsRunId: RunId = NoRunId
28962899
private var myAtoms: Set[Type] = _
2900+
private var myWidenedAtoms: Set[Type] = _
28972901
private var myWidened: Type = _
28982902

28992903
private def ensureAtomsComputed()(implicit ctx: Context): Unit =
29002904
if (atomsRunId != ctx.runId) {
2901-
val atoms1 = tp1.atoms
2902-
val atoms2 = tp2.atoms
2905+
val atoms1 = tp1.atoms()
2906+
val atoms2 = tp2.atoms()
29032907
myAtoms = if (atoms1.nonEmpty && atoms2.nonEmpty) atoms1 | atoms2 else Set.empty
2908+
val widenedAtoms1 = tp1.atoms(widenOK = true)
2909+
val widenedAtoms2 = tp2.atoms(widenOK = true)
2910+
myWidenedAtoms = if (widenedAtoms1.nonEmpty && widenedAtoms2.nonEmpty) widenedAtoms1 | widenedAtoms2 else Set.empty
29042911
val tp1w = tp1.widenSingletons
29052912
val tp2w = tp2.widenSingletons
29062913
myWidened = if ((tp1 eq tp1w) && (tp2 eq tp2w)) this else tp1w | tp2w
29072914
atomsRunId = ctx.runId
29082915
}
29092916

2910-
override def atoms(implicit ctx: Context): Set[Type] = {
2917+
override def atoms(widenOK: Boolean)(implicit ctx: Context): Set[Type] = {
29112918
ensureAtomsComputed()
2912-
myAtoms
2919+
if widenOK then myAtoms else myWidenedAtoms
29132920
}
29142921

29152922
override def widenSingletons(implicit ctx: Context): Type = {

tests/pos/i8129.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Test {
2+
type F[N <: 0 | 1] = N
3+
def fl[N <: 0 | 1]: F[N] = ???
4+
}

0 commit comments

Comments
 (0)