Skip to content

Commit 3bc3d87

Browse files
committed
Avoid cycle between findMember and &
As test case IterableDotty shows, there can be a cycle findMember --> & --> <:< --> hasMatchingMember --> findMember which leads to an infinite recursion. This commit avoids this by keeping track of names of outstanding findMember searches and falling back to forming AndTypes instead of `&' is we repeat a findMember search on a name seen before after a certain recursion depth.
1 parent f8d65ff commit 3bc3d87

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
2525
private var pendingSubTypes: mutable.Set[(Type, Type)] = null
2626
private var recCount = 0
2727

28+
private[core] var findMemberCount = 0
29+
private[core] var pendingMemberSearches: List[Name] = Nil
30+
2831
private var needsGc = false
2932

3033
/** Is a subtype check in progress? In that case we may not

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

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ import language.implicitConversions
3535

3636
object Types {
3737

38-
private var recCount = 0 // used temporarily for debugging. TODO: remove
39-
4038
private var nextId = 0
4139

4240
/** The class of types.
@@ -417,9 +415,7 @@ object Types {
417415
* as seen from given prefix `pre`. Exclude all members that have
418416
* flags in `excluded` from consideration.
419417
*/
420-
final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = try {
421-
recCount += 1
422-
assert(recCount < 40)
418+
final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = {
423419
@tailrec def go(tp: Type): Denotation = tp match {
424420
case tp: RefinedType =>
425421
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
@@ -456,7 +452,11 @@ object Types {
456452
if (tp.refinementRefersToThis) tp.refinedInfo.substSkolem(tp, pre)
457453
else tp.refinedInfo
458454
if (name.isTypeName) { // simplified case that runs more efficiently
459-
val jointInfo = if (rinfo.isAlias) rinfo else pdenot.info & rinfo
455+
val jointInfo =
456+
if (rinfo.isAlias) rinfo
457+
else if (pdenot.info.isAlias) pdenot.info
458+
else if (ctx.typeComparer.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo)
459+
else pdenot.info & rinfo
460460
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
461461
} else
462462
pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre)
@@ -488,15 +488,30 @@ object Types {
488488
}
489489
def goAnd(l: Type, r: Type) = go(l) & (go(r), pre)
490490
def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
491-
go(this)
492-
} catch {
493-
case ex: MergeError =>
494-
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
495-
case ex: Throwable =>
496-
println(s"findMember exception for $this member $name")
497-
throw ex // DEBUG
498-
} finally {
499-
recCount -= 1
491+
def safeAnd(tp1: Type, tp2: Type): Type = (tp1, tp2) match {
492+
case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2))
493+
case _ => tp1 & tp2
494+
}
495+
496+
val cmp = ctx.typeComparer
497+
val recCount = cmp.findMemberCount
498+
cmp.findMemberCount = recCount + 1
499+
if (recCount >= Config.LogPendingFindMemberThreshold) {
500+
cmp.pendingMemberSearches = name :: cmp.pendingMemberSearches
501+
}
502+
try go(this)
503+
catch {
504+
case ex: MergeError =>
505+
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
506+
case ex: Throwable =>
507+
println(i"findMember exception for $this member $name")
508+
throw ex // DEBUG
509+
}
510+
finally {
511+
cmp.findMemberCount = recCount - 1
512+
if (recCount >= Config.LogPendingFindMemberThreshold)
513+
cmp.pendingMemberSearches = cmp.pendingMemberSearches.tail
514+
}
500515
}
501516

502517
/** The set of names of members of this type that pass the given name filter

0 commit comments

Comments
 (0)