Skip to content

Commit dd4a235

Browse files
authored
Merge pull request #12928 from dotty-staging/fix-12915
Balance And/Or types when forming lubs and glbs
2 parents f33bc8d + 15d54c9 commit dd4a235

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
@@ -2242,7 +2243,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
22422243
* opportunistically merged.
22432244
*/
22442245
final def andType(tp1: Type, tp2: Type, isErased: Boolean = ctx.erasedTypes): Type =
2245-
andTypeGen(tp1, tp2, AndType(_, _), isErased = isErased)
2246+
andTypeGen(tp1, tp2, AndType.balanced(_, _), isErased = isErased)
22462247

22472248
final def simplifyAndTypeWithFallback(tp1: Type, tp2: Type, fallback: Type): Type =
22482249
andTypeGen(tp1, tp2, (_, _) => fallback)
@@ -2264,7 +2265,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
22642265
val t2 = distributeOr(tp2, tp1, isSoft)
22652266
if (t2.exists) t2
22662267
else if (isErased) erasedLub(tp1, tp2)
2267-
else liftIfHK(tp1, tp2, OrType(_, _, soft = isSoft), _ | _, _ & _)
2268+
else liftIfHK(tp1, tp2, OrType.balanced(_, _, soft = isSoft), _ | _, _ & _)
22682269
}
22692270
}
22702271

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

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

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

17181726
/** Substitute all types that refer in their symbol attribute to
@@ -3107,6 +3115,12 @@ object Types {
31073115
myBaseClasses
31083116
}
31093117

3118+
private var myFactorCount = 0
3119+
override def andFactorCount =
3120+
if myFactorCount == 0 then
3121+
myFactorCount = tp1.andFactorCount + tp2.andFactorCount
3122+
myFactorCount
3123+
31103124
def derivedAndType(tp1: Type, tp2: Type)(using Context): Type =
31113125
if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this
31123126
else AndType.make(tp1, tp2, checkValid = true)
@@ -3132,6 +3146,23 @@ object Types {
31323146
unchecked(tp1, tp2)
31333147
}
31343148

3149+
def balanced(tp1: Type, tp2: Type)(using Context): AndType =
3150+
tp1 match
3151+
case AndType(tp11, tp12) if tp1.andFactorCount > tp2.andFactorCount * 2 =>
3152+
if tp11.andFactorCount < tp12.andFactorCount then
3153+
return apply(tp12, balanced(tp11, tp2))
3154+
else
3155+
return apply(tp11, balanced(tp12, tp2))
3156+
case _ =>
3157+
tp2 match
3158+
case AndType(tp21, tp22) if tp2.andFactorCount > tp1.andFactorCount * 2 =>
3159+
if tp22.andFactorCount < tp21.andFactorCount then
3160+
return apply(balanced(tp1, tp22), tp21)
3161+
else
3162+
return apply(balanced(tp1, tp21), tp22)
3163+
case _ =>
3164+
apply(tp1, tp2)
3165+
31353166
def unchecked(tp1: Type, tp2: Type)(using Context): AndType = {
31363167
assertUnerased()
31373168
unique(new CachedAndType(tp1, tp2))
@@ -3178,6 +3209,14 @@ object Types {
31783209
myBaseClasses
31793210
}
31803211

3212+
private var myFactorCount = 0
3213+
override def orFactorCount(soft: Boolean) =
3214+
if this.isSoft == soft then
3215+
if myFactorCount == 0 then
3216+
myFactorCount = tp1.orFactorCount(soft) + tp2.orFactorCount(soft)
3217+
myFactorCount
3218+
else 1
3219+
31813220
assert(tp1.isValueTypeOrWildcard &&
31823221
tp2.isValueTypeOrWildcard, s"$tp1 $tp2")
31833222

@@ -3249,10 +3288,29 @@ object Types {
32493288
final class CachedOrType(tp1: Type, tp2: Type, override val isSoft: Boolean) extends OrType(tp1, tp2)
32503289

32513290
object OrType {
3291+
32523292
def apply(tp1: Type, tp2: Type, soft: Boolean)(using Context): OrType = {
32533293
assertUnerased()
32543294
unique(new CachedOrType(tp1, tp2, soft))
32553295
}
3296+
3297+
def balanced(tp1: Type, tp2: Type, soft: Boolean)(using Context): OrType =
3298+
tp1 match
3299+
case OrType(tp11, tp12) if tp1.orFactorCount(soft) > tp2.orFactorCount(soft) * 2 =>
3300+
if tp11.orFactorCount(soft) < tp12.orFactorCount(soft) then
3301+
return apply(tp12, balanced(tp11, tp2, soft), soft)
3302+
else
3303+
return apply(tp11, balanced(tp12, tp2, soft), soft)
3304+
case _ =>
3305+
tp2 match
3306+
case OrType(tp21, tp22) if tp2.orFactorCount(soft) > tp1.orFactorCount(soft) * 2 =>
3307+
if tp22.orFactorCount(soft) < tp21.orFactorCount(soft) then
3308+
return apply(balanced(tp1, tp22, soft), tp21, soft)
3309+
else
3310+
return apply(balanced(tp1, tp21, soft), tp22, soft)
3311+
case _ =>
3312+
apply(tp1, tp2, soft)
3313+
32563314
def make(tp1: Type, tp2: Type, soft: Boolean)(using Context): Type =
32573315
if (tp1 eq tp2) tp1
32583316
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)