Skip to content

Commit 15d54c9

Browse files
committed
Balance And/Or types when forming lubs and glbs
Fixes #12915
1 parent 2ccf681 commit 15d54c9

File tree

3 files changed

+166
-2
lines changed

3 files changed

+166
-2
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
15711571
* @see [[sufficientEither]] for the normal case
15721572
*/
15731573
protected def either(op1: => Boolean, op2: => Boolean): Boolean =
1574+
Stats.record("TypeComparer.either")
15741575
if ctx.mode.is(Mode.GadtConstraintInference) || useNecessaryEither then
15751576
necessaryEither(op1, op2)
15761577
else
@@ -2238,7 +2239,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
22382239
* opportunistically merged.
22392240
*/
22402241
final def andType(tp1: Type, tp2: Type, isErased: Boolean = ctx.erasedTypes): Type =
2241-
andTypeGen(tp1, tp2, AndType(_, _), isErased = isErased)
2242+
andTypeGen(tp1, tp2, AndType.balanced(_, _), isErased = isErased)
22422243

22432244
final def simplifyAndTypeWithFallback(tp1: Type, tp2: Type, fallback: Type): Type =
22442245
andTypeGen(tp1, tp2, (_, _) => fallback)
@@ -2260,7 +2261,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
22602261
val t2 = distributeOr(tp2, tp1, isSoft)
22612262
if (t2.exists) t2
22622263
else if (isErased) erasedLub(tp1, tp2)
2263-
else liftIfHK(tp1, tp2, OrType(_, _, soft = isSoft), _ | _, _ & _)
2264+
else liftIfHK(tp1, tp2, OrType.balanced(_, _, soft = isSoft), _ | _, _ & _)
22642265
}
22652266
}
22662267

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,14 @@ object Types {
17161716
/** If this is a proto type, WildcardType, otherwise the type itself */
17171717
def dropIfProto: Type = this
17181718

1719+
/** If this is an AndType, the number of factors, 1 for all other types */
1720+
def andFactorCount: Int = 1
1721+
1722+
/** If this is a OrType, the number of factors if that match `soft`,
1723+
* 1 for all other types.
1724+
*/
1725+
def orFactorCount(soft: Boolean): Int = 1
1726+
17191727
// ----- Substitutions -----------------------------------------------------
17201728

17211729
/** Substitute all types that refer in their symbol attribute to
@@ -3110,6 +3118,12 @@ object Types {
31103118
myBaseClasses
31113119
}
31123120

3121+
private var myFactorCount = 0
3122+
override def andFactorCount =
3123+
if myFactorCount == 0 then
3124+
myFactorCount = tp1.andFactorCount + tp2.andFactorCount
3125+
myFactorCount
3126+
31133127
def derivedAndType(tp1: Type, tp2: Type)(using Context): Type =
31143128
if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this
31153129
else AndType.make(tp1, tp2, checkValid = true)
@@ -3135,6 +3149,23 @@ object Types {
31353149
unchecked(tp1, tp2)
31363150
}
31373151

3152+
def balanced(tp1: Type, tp2: Type)(using Context): AndType =
3153+
tp1 match
3154+
case AndType(tp11, tp12) if tp1.andFactorCount > tp2.andFactorCount * 2 =>
3155+
if tp11.andFactorCount < tp12.andFactorCount then
3156+
return apply(tp12, balanced(tp11, tp2))
3157+
else
3158+
return apply(tp11, balanced(tp12, tp2))
3159+
case _ =>
3160+
tp2 match
3161+
case AndType(tp21, tp22) if tp2.andFactorCount > tp1.andFactorCount * 2 =>
3162+
if tp22.andFactorCount < tp21.andFactorCount then
3163+
return apply(balanced(tp1, tp22), tp21)
3164+
else
3165+
return apply(balanced(tp1, tp21), tp22)
3166+
case _ =>
3167+
apply(tp1, tp2)
3168+
31383169
def unchecked(tp1: Type, tp2: Type)(using Context): AndType = {
31393170
assertUnerased()
31403171
unique(new CachedAndType(tp1, tp2))
@@ -3181,6 +3212,14 @@ object Types {
31813212
myBaseClasses
31823213
}
31833214

3215+
private var myFactorCount = 0
3216+
override def orFactorCount(soft: Boolean) =
3217+
if this.isSoft == soft then
3218+
if myFactorCount == 0 then
3219+
myFactorCount = tp1.orFactorCount(soft) + tp2.orFactorCount(soft)
3220+
myFactorCount
3221+
else 1
3222+
31843223
assert(tp1.isValueTypeOrWildcard &&
31853224
tp2.isValueTypeOrWildcard, s"$tp1 $tp2")
31863225

@@ -3238,10 +3277,29 @@ object Types {
32383277
final class CachedOrType(tp1: Type, tp2: Type, override val isSoft: Boolean) extends OrType(tp1, tp2)
32393278

32403279
object OrType {
3280+
32413281
def apply(tp1: Type, tp2: Type, soft: Boolean)(using Context): OrType = {
32423282
assertUnerased()
32433283
unique(new CachedOrType(tp1, tp2, soft))
32443284
}
3285+
3286+
def balanced(tp1: Type, tp2: Type, soft: Boolean)(using Context): OrType =
3287+
tp1 match
3288+
case OrType(tp11, tp12) if tp1.orFactorCount(soft) > tp2.orFactorCount(soft) * 2 =>
3289+
if tp11.orFactorCount(soft) < tp12.orFactorCount(soft) then
3290+
return apply(tp12, balanced(tp11, tp2, soft), soft)
3291+
else
3292+
return apply(tp11, balanced(tp12, tp2, soft), soft)
3293+
case _ =>
3294+
tp2 match
3295+
case OrType(tp21, tp22) if tp2.orFactorCount(soft) > tp1.orFactorCount(soft) * 2 =>
3296+
if tp22.orFactorCount(soft) < tp21.orFactorCount(soft) then
3297+
return apply(balanced(tp1, tp22, soft), tp21, soft)
3298+
else
3299+
return apply(balanced(tp1, tp21, soft), tp22, soft)
3300+
case _ =>
3301+
apply(tp1, tp2, soft)
3302+
32453303
def make(tp1: Type, tp2: Type, soft: Boolean)(using Context): Type =
32463304
if (tp1 eq tp2) tp1
32473305
else apply(tp1, tp2, soft)

tests/pos/i12915.scala

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
trait E[T]
2+
3+
class X {
4+
val e1: E[Int] = ???
5+
val e2: E[String] = ???
6+
val e3: E[List[Int]] = ???
7+
val e4: E[List[String]] = ???
8+
val e5: E[Double] = ???
9+
val e6: E[(String, String)] = ???
10+
val e7: E[(String, Int)] = ???
11+
val e8: E[(Int, List[String])] = ???
12+
val e9: E[Long] = ???
13+
val e10: E[(Long, Long)] = ???
14+
val e11: E[(Long, Long, Int)] = ???
15+
val e12: E[List[Long]] = ???
16+
val e13: E[List[Int]] = ???
17+
val e14: E[(String, String)] = ???
18+
val e15: E[(String, String, String)] = ???
19+
val e16: E[(Int, String)] = ???
20+
val e17: E[(String, Long, String)] = ???
21+
val e18: E[(Long, String, String)] = ???
22+
val e19: E[(String, String, Long)] = ???
23+
val e20: E[(String, Int, String)] = ???
24+
val e21: E[(Int, String, String)] = ???
25+
val e22: E[(String, String, Int)] = ???
26+
val e23: E[(String, String, Boolean)] = ???
27+
val e24: E[(Boolean, Boolean, String)] = ???
28+
val e25: E[(String, Int, Boolean)] = ???
29+
val e26: E[List[(String, String)]] = ???
30+
val e27: E[List[(Int, String)]] = ???
31+
val e28: E[List[(String, Int)]] = ???
32+
val e29: E[List[(Long, String)]] = ???
33+
val e30: E[List[(String, Long)]] = ???
34+
val e31: E[List[(Boolean, String)]] = ???
35+
val e32: E[List[(String, Boolean)]] = ???
36+
val e33: E[List[((String, String), String)]] = ???
37+
val e34: E[List[((String, Int), String)]] = ???
38+
val e35: E[List[((Long, String), String)]] = ???
39+
val e36: E[List[((Boolean, String), String)]] = ???
40+
val e37: E[List[((String, String), Int)]] = ???
41+
val e38: E[List[((String, String), (String, Int))]] = ???
42+
val e39: E[List[((Boolean, Long), (String, Int))]] = ???
43+
val e40: E[List[((Int, Long), (Boolean, Int))]] = ???
44+
val e41: E[List[((String, (Int, String)), (String, Int))]] = ???
45+
val e42: E[List[((Boolean, (Int, String)), (String, Int))]] = ???
46+
val e43: E[List[((String, (Int, String)), (Boolean, Int))]] = ???
47+
val e44: E[(Int, List[String], Long)] = ???
48+
val e45: E[(Int, List[Int], Long)] = ???
49+
val e46: E[(Int, List[Long], Long)] = ???
50+
val e47: E[(String, List[String], Long)] = ???
51+
val e48: E[(Int, List[String], Boolean)] = ???
52+
val e49: E[Char] = ???
53+
54+
val all = List(
55+
e1,
56+
e2,
57+
e3,
58+
e4,
59+
e5,
60+
e6,
61+
e7,
62+
e8,
63+
e9,
64+
e10,
65+
e11,
66+
e12,
67+
e13,
68+
e14,
69+
e15,
70+
e16,
71+
e17,
72+
e18,
73+
e19,
74+
e20,
75+
e21,
76+
e22,
77+
e23,
78+
e24,
79+
e25,
80+
e26,
81+
e27,
82+
e28,
83+
e29,
84+
e30,
85+
e31,
86+
e32,
87+
e33,
88+
e34,
89+
e35,
90+
e36,
91+
e37,
92+
e38,
93+
e39,
94+
e40,
95+
e41,
96+
e42,
97+
e43,
98+
e44,
99+
e45,
100+
e46,
101+
e47,
102+
e48,
103+
e49
104+
)
105+
}

0 commit comments

Comments
 (0)