Skip to content
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

Optimize isSameType for invariant applied types with the same structure #15556

Merged
merged 2 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import Phases.{gettersPhase, elimByNamePhase}
import StdNames.nme
import TypeOps.refineUsingParent
import collection.mutable
import util.Stats
import util.NoSourcePosition
import util.{Stats, NoSourcePosition, EqHashMap}
import config.Config
import config.Feature.migrateTo3
import config.Printers.{subtyping, gadts, matchTypes, noPrinter}
Expand Down Expand Up @@ -163,6 +162,20 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
/** A flag to prevent recursive joins when comparing AndTypes on the left */
private var joined = false

/** A variable to keep track of number of outstanding isSameType tests */
private var sameLevel = 0

/** A map that records successful isSameType comparisons.
* Used together with `sameLevel` to avoid exponential blowUp of isSameType
* comparisons for deeply nested invariant applied types.
*/
private var sames: util.EqHashMap[Type, Type] | Null = null

/** The `sameLevel` nesting depth from which on we want to keep track
* of isSameTypes suucesses using `sames`
*/
val startSameTypeTrackingLevel = 3

private inline def inFrozenGadtIf[T](cond: Boolean)(inline op: T): T = {
val savedFrozenGadt = frozenGadt
frozenGadt ||= cond
Expand Down Expand Up @@ -1553,8 +1566,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
&& defn.isByNameFunction(arg2.dealias) =>
isSubArg(arg1res, arg2.argInfos.head)
case _ =>
(v > 0 || isSubType(arg2, arg1)) &&
(v < 0 || isSubType(arg1, arg2))
if v < 0 then isSubType(arg2, arg1)
else if v > 0 then isSubType(arg1, arg2)
else isSameType(arg2, arg1)

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

// Type equality =:=

/** Two types are the same if are mutual subtypes of each other */
/** Two types are the same if they are mutual subtypes of each other.
* To avoid exponential blowup for deeply nested invariant applied types,
* we cache successes once the stack of outstanding isSameTypes reaches
* depth `startSameTypeTrackingLevel`. See pos/i15525.scala, where this matters.
*/
def isSameType(tp1: Type, tp2: Type): Boolean =
if (tp1 eq NoType) false
else if (tp1 eq tp2) true
else isSubType(tp1, tp2) && isSubType(tp2, tp1)
if tp1 eq NoType then false
else if tp1 eq tp2 then true
else if sames != null && (sames.nn.lookup(tp1) eq tp2) then true
else
val savedSames = sames
sameLevel += 1
if sameLevel >= startSameTypeTrackingLevel then
Stats.record("cache same type")
sames = new util.EqHashMap()
val res =
try isSubType(tp1, tp2) && isSubType(tp2, tp1)
finally
sameLevel -= 1
sames = savedSames
if res && sames != null then sames.nn(tp2) = tp1
res

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

Expand Down
53 changes: 53 additions & 0 deletions tests/neg/i15525.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
class /[D, T]
class Delegating[D]

type Aux[E] = Container { type Elements = E }

class Container:
type Elements = Delegating[Delegates]
type Delegates

class Resolution[E](value: Aux[E]):
type Type = Aux[E]

def element22(
transmittable0: Resolution[?], transmittable1: Resolution[?],
transmittable2: Resolution[?], transmittable3: Resolution[?],
transmittable4: Resolution[?], transmittable5: Resolution[?],
transmittable6: Resolution[?], transmittable7: Resolution[?],
transmittable8: Resolution[?], transmittable9: Resolution[?],
transmittable10: Resolution[?], transmittable11: Resolution[?],
transmittable12: Resolution[?], transmittable13: Resolution[?],
transmittable14: Resolution[?], transmittable15: Resolution[?],
transmittable16: Resolution[?], transmittable17: Resolution[?],
transmittable18: Resolution[?], transmittable19: Resolution[?],
transmittable20: Resolution[?], transmittable21: Resolution[?])
: Container {
type Delegates =
transmittable0.Type / transmittable1.Type /
transmittable2.Type / transmittable3.Type /
transmittable4.Type / transmittable5.Type /
transmittable6.Type / transmittable7.Type /
transmittable8.Type / transmittable9.Type /
transmittable10.Type / transmittable11.Type /
transmittable12.Type / transmittable13.Type /
transmittable14.Type / transmittable15.Type /
transmittable16.Type / transmittable17.Type /
transmittable18.Type / transmittable19.Type /
transmittable20.Type / transmittable21.Type
} = ???

def test22 =
Resolution(
element22(
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0), // error // error
Resolution(element0), Resolution(element0)))// error // error
55 changes: 55 additions & 0 deletions tests/pos/i15525.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
class /[D, T]
class Delegating[D]

type Aux[E] = Container { type Elements = E }

class Container:
type Elements = Delegating[Delegates]
type Delegates

class Resolution[E](value: Aux[E]):
type Type = Aux[E]

def element0: Container { type Delegates = Unit } = ???

def element22(
transmittable0: Resolution[?], transmittable1: Resolution[?],
transmittable2: Resolution[?], transmittable3: Resolution[?],
transmittable4: Resolution[?], transmittable5: Resolution[?],
transmittable6: Resolution[?], transmittable7: Resolution[?],
transmittable8: Resolution[?], transmittable9: Resolution[?],
transmittable10: Resolution[?], transmittable11: Resolution[?],
transmittable12: Resolution[?], transmittable13: Resolution[?],
transmittable14: Resolution[?], transmittable15: Resolution[?],
transmittable16: Resolution[?], transmittable17: Resolution[?],
transmittable18: Resolution[?], transmittable19: Resolution[?],
transmittable20: Resolution[?], transmittable21: Resolution[?])
: Container {
type Delegates =
transmittable0.Type / transmittable1.Type /
transmittable2.Type / transmittable3.Type /
transmittable4.Type / transmittable5.Type /
transmittable6.Type / transmittable7.Type /
transmittable8.Type / transmittable9.Type /
transmittable10.Type / transmittable11.Type /
transmittable12.Type / transmittable13.Type /
transmittable14.Type / transmittable15.Type /
transmittable16.Type / transmittable17.Type /
transmittable18.Type / transmittable19.Type /
transmittable20.Type / transmittable21.Type
} = ???

def test22 =
Resolution(
element22(
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0)))