aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/Types.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-05-18 19:45:03 +0200
committerMartin Odersky <odersky@gmail.com>2015-05-21 17:41:16 +0200
commit02f5ba8f72b043854aec5729f2b30fe813027435 (patch)
tree4a046f25b09b6ebb903d393e269ca9df30d33719 /src/dotty/tools/dotc/core/Types.scala
parent709c657b3963ec3d3d4f0933fe05bf25f697b48f (diff)
downloaddotty-02f5ba8f72b043854aec5729f2b30fe813027435.tar.gz
dotty-02f5ba8f72b043854aec5729f2b30fe813027435.tar.bz2
dotty-02f5ba8f72b043854aec5729f2b30fe813027435.zip
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.
Diffstat (limited to 'src/dotty/tools/dotc/core/Types.scala')
-rw-r--r--src/dotty/tools/dotc/core/Types.scala45
1 files changed, 30 insertions, 15 deletions
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 792029ee4..9d1385c72 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -35,8 +35,6 @@ import language.implicitConversions
object Types {
- private var recCount = 0 // used temporarily for debugging. TODO: remove
-
private var nextId = 0
/** The class of types.
@@ -417,9 +415,7 @@ object Types {
* as seen from given prefix `pre`. Exclude all members that have
* flags in `excluded` from consideration.
*/
- final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = try {
- recCount += 1
- assert(recCount < 40)
+ final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = {
@tailrec def go(tp: Type): Denotation = tp match {
case tp: RefinedType =>
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
@@ -456,7 +452,11 @@ object Types {
if (tp.refinementRefersToThis) tp.refinedInfo.substSkolem(tp, pre)
else tp.refinedInfo
if (name.isTypeName) { // simplified case that runs more efficiently
- val jointInfo = if (rinfo.isAlias) rinfo else pdenot.info & rinfo
+ val jointInfo =
+ if (rinfo.isAlias) rinfo
+ else if (pdenot.info.isAlias) pdenot.info
+ else if (ctx.typeComparer.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo)
+ else pdenot.info & rinfo
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
} else
pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre)
@@ -488,15 +488,30 @@ object Types {
}
def goAnd(l: Type, r: Type) = go(l) & (go(r), pre)
def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
- go(this)
- } catch {
- case ex: MergeError =>
- throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
- case ex: Throwable =>
- println(s"findMember exception for $this member $name")
- throw ex // DEBUG
- } finally {
- recCount -= 1
+ def safeAnd(tp1: Type, tp2: Type): Type = (tp1, tp2) match {
+ case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2))
+ case _ => tp1 & tp2
+ }
+
+ val cmp = ctx.typeComparer
+ val recCount = cmp.findMemberCount
+ cmp.findMemberCount = recCount + 1
+ if (recCount >= Config.LogPendingFindMemberThreshold) {
+ cmp.pendingMemberSearches = name :: cmp.pendingMemberSearches
+ }
+ try go(this)
+ catch {
+ case ex: MergeError =>
+ throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
+ case ex: Throwable =>
+ println(i"findMember exception for $this member $name")
+ throw ex // DEBUG
+ }
+ finally {
+ cmp.findMemberCount = recCount - 1
+ if (recCount >= Config.LogPendingFindMemberThreshold)
+ cmp.pendingMemberSearches = cmp.pendingMemberSearches.tail
+ }
}
/** The set of names of members of this type that pass the given name filter