Skip to content

Commit f157978

Browse files
authored
Merge pull request #15556 from dotty-staging/fix-15525
Optimize isSameType for invariant applied types with the same structure
2 parents fad3175 + a54fac4 commit f157978

File tree

3 files changed

+147
-8
lines changed

3 files changed

+147
-8
lines changed

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

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import Phases.{gettersPhase, elimByNamePhase}
88
import StdNames.nme
99
import TypeOps.refineUsingParent
1010
import collection.mutable
11-
import util.Stats
12-
import util.NoSourcePosition
11+
import util.{Stats, NoSourcePosition, EqHashMap}
1312
import config.Config
1413
import config.Feature.migrateTo3
1514
import config.Printers.{subtyping, gadts, matchTypes, noPrinter}
@@ -163,6 +162,20 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
163162
/** A flag to prevent recursive joins when comparing AndTypes on the left */
164163
private var joined = false
165164

165+
/** A variable to keep track of number of outstanding isSameType tests */
166+
private var sameLevel = 0
167+
168+
/** A map that records successful isSameType comparisons.
169+
* Used together with `sameLevel` to avoid exponential blowUp of isSameType
170+
* comparisons for deeply nested invariant applied types.
171+
*/
172+
private var sames: util.EqHashMap[Type, Type] | Null = null
173+
174+
/** The `sameLevel` nesting depth from which on we want to keep track
175+
* of isSameTypes suucesses using `sames`
176+
*/
177+
val startSameTypeTrackingLevel = 3
178+
166179
private inline def inFrozenGadtIf[T](cond: Boolean)(inline op: T): T = {
167180
val savedFrozenGadt = frozenGadt
168181
frozenGadt ||= cond
@@ -1553,8 +1566,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
15531566
&& defn.isByNameFunction(arg2.dealias) =>
15541567
isSubArg(arg1res, arg2.argInfos.head)
15551568
case _ =>
1556-
(v > 0 || isSubType(arg2, arg1)) &&
1557-
(v < 0 || isSubType(arg1, arg2))
1569+
if v < 0 then isSubType(arg2, arg1)
1570+
else if v > 0 then isSubType(arg1, arg2)
1571+
else isSameType(arg2, arg1)
15581572

15591573
isSubArg(args1.head, args2.head)
15601574
} && recurArgs(args1.tail, args2.tail, tparams2.tail)
@@ -2012,11 +2026,28 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
20122026

20132027
// Type equality =:=
20142028

2015-
/** Two types are the same if are mutual subtypes of each other */
2029+
/** Two types are the same if they are mutual subtypes of each other.
2030+
* To avoid exponential blowup for deeply nested invariant applied types,
2031+
* we cache successes once the stack of outstanding isSameTypes reaches
2032+
* depth `startSameTypeTrackingLevel`. See pos/i15525.scala, where this matters.
2033+
*/
20162034
def isSameType(tp1: Type, tp2: Type): Boolean =
2017-
if (tp1 eq NoType) false
2018-
else if (tp1 eq tp2) true
2019-
else isSubType(tp1, tp2) && isSubType(tp2, tp1)
2035+
if tp1 eq NoType then false
2036+
else if tp1 eq tp2 then true
2037+
else if sames != null && (sames.nn.lookup(tp1) eq tp2) then true
2038+
else
2039+
val savedSames = sames
2040+
sameLevel += 1
2041+
if sameLevel >= startSameTypeTrackingLevel then
2042+
Stats.record("cache same type")
2043+
sames = new util.EqHashMap()
2044+
val res =
2045+
try isSubType(tp1, tp2) && isSubType(tp2, tp1)
2046+
finally
2047+
sameLevel -= 1
2048+
sames = savedSames
2049+
if res && sames != null then sames.nn(tp2) = tp1
2050+
res
20202051

20212052
override protected def isSame(tp1: Type, tp2: Type)(using Context): Boolean = isSameType(tp1, tp2)
20222053

tests/neg/i15525.scala

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
class /[D, T]
2+
class Delegating[D]
3+
4+
type Aux[E] = Container { type Elements = E }
5+
6+
class Container:
7+
type Elements = Delegating[Delegates]
8+
type Delegates
9+
10+
class Resolution[E](value: Aux[E]):
11+
type Type = Aux[E]
12+
13+
def element22(
14+
transmittable0: Resolution[?], transmittable1: Resolution[?],
15+
transmittable2: Resolution[?], transmittable3: Resolution[?],
16+
transmittable4: Resolution[?], transmittable5: Resolution[?],
17+
transmittable6: Resolution[?], transmittable7: Resolution[?],
18+
transmittable8: Resolution[?], transmittable9: Resolution[?],
19+
transmittable10: Resolution[?], transmittable11: Resolution[?],
20+
transmittable12: Resolution[?], transmittable13: Resolution[?],
21+
transmittable14: Resolution[?], transmittable15: Resolution[?],
22+
transmittable16: Resolution[?], transmittable17: Resolution[?],
23+
transmittable18: Resolution[?], transmittable19: Resolution[?],
24+
transmittable20: Resolution[?], transmittable21: Resolution[?])
25+
: Container {
26+
type Delegates =
27+
transmittable0.Type / transmittable1.Type /
28+
transmittable2.Type / transmittable3.Type /
29+
transmittable4.Type / transmittable5.Type /
30+
transmittable6.Type / transmittable7.Type /
31+
transmittable8.Type / transmittable9.Type /
32+
transmittable10.Type / transmittable11.Type /
33+
transmittable12.Type / transmittable13.Type /
34+
transmittable14.Type / transmittable15.Type /
35+
transmittable16.Type / transmittable17.Type /
36+
transmittable18.Type / transmittable19.Type /
37+
transmittable20.Type / transmittable21.Type
38+
} = ???
39+
40+
def test22 =
41+
Resolution(
42+
element22(
43+
Resolution(element0), Resolution(element0), // error // error
44+
Resolution(element0), Resolution(element0), // error // error
45+
Resolution(element0), Resolution(element0), // error // error
46+
Resolution(element0), Resolution(element0), // error // error
47+
Resolution(element0), Resolution(element0), // error // error
48+
Resolution(element0), Resolution(element0), // error // error
49+
Resolution(element0), Resolution(element0), // error // error
50+
Resolution(element0), Resolution(element0), // error // error
51+
Resolution(element0), Resolution(element0), // error // error
52+
Resolution(element0), Resolution(element0), // error // error
53+
Resolution(element0), Resolution(element0)))// error // error

tests/pos/i15525.scala

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
class /[D, T]
2+
class Delegating[D]
3+
4+
type Aux[E] = Container { type Elements = E }
5+
6+
class Container:
7+
type Elements = Delegating[Delegates]
8+
type Delegates
9+
10+
class Resolution[E](value: Aux[E]):
11+
type Type = Aux[E]
12+
13+
def element0: Container { type Delegates = Unit } = ???
14+
15+
def element22(
16+
transmittable0: Resolution[?], transmittable1: Resolution[?],
17+
transmittable2: Resolution[?], transmittable3: Resolution[?],
18+
transmittable4: Resolution[?], transmittable5: Resolution[?],
19+
transmittable6: Resolution[?], transmittable7: Resolution[?],
20+
transmittable8: Resolution[?], transmittable9: Resolution[?],
21+
transmittable10: Resolution[?], transmittable11: Resolution[?],
22+
transmittable12: Resolution[?], transmittable13: Resolution[?],
23+
transmittable14: Resolution[?], transmittable15: Resolution[?],
24+
transmittable16: Resolution[?], transmittable17: Resolution[?],
25+
transmittable18: Resolution[?], transmittable19: Resolution[?],
26+
transmittable20: Resolution[?], transmittable21: Resolution[?])
27+
: Container {
28+
type Delegates =
29+
transmittable0.Type / transmittable1.Type /
30+
transmittable2.Type / transmittable3.Type /
31+
transmittable4.Type / transmittable5.Type /
32+
transmittable6.Type / transmittable7.Type /
33+
transmittable8.Type / transmittable9.Type /
34+
transmittable10.Type / transmittable11.Type /
35+
transmittable12.Type / transmittable13.Type /
36+
transmittable14.Type / transmittable15.Type /
37+
transmittable16.Type / transmittable17.Type /
38+
transmittable18.Type / transmittable19.Type /
39+
transmittable20.Type / transmittable21.Type
40+
} = ???
41+
42+
def test22 =
43+
Resolution(
44+
element22(
45+
Resolution(element0), Resolution(element0),
46+
Resolution(element0), Resolution(element0),
47+
Resolution(element0), Resolution(element0),
48+
Resolution(element0), Resolution(element0),
49+
Resolution(element0), Resolution(element0),
50+
Resolution(element0), Resolution(element0),
51+
Resolution(element0), Resolution(element0),
52+
Resolution(element0), Resolution(element0),
53+
Resolution(element0), Resolution(element0),
54+
Resolution(element0), Resolution(element0),
55+
Resolution(element0), Resolution(element0)))

0 commit comments

Comments
 (0)