aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2015-12-30 20:45:21 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-01-18 16:49:16 +0100
commit06e18c6e7761c458b33af3471f013a4dd3cee3f1 (patch)
tree21e8cf8e21ebdca1cef7252d5cdc83de407ba66b
parent3c29bbe7953f31f35dd404577cd04b4de95f74bb (diff)
downloaddotty-06e18c6e7761c458b33af3471f013a4dd3cee3f1.tar.gz
dotty-06e18c6e7761c458b33af3471f013a4dd3cee3f1.tar.bz2
dotty-06e18c6e7761c458b33af3471f013a4dd3cee3f1.zip
Avoid infinite subtyping checks when intersecting denotations
This allows us to run compileStdLib without deep subtypes again.
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala11
-rw-r--r--src/dotty/tools/dotc/core/Types.scala33
-rw-r--r--test/dotc/tests.scala2
3 files changed, 33 insertions, 13 deletions
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index fcd60ef16..00ad90354 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -259,11 +259,12 @@ object Denotations {
*
* If there is no preferred accessible denotation, return a JointRefDenotation
* with one of the operand symbols (unspecified which one), and an info which
- * is intersection (&) of the infos of the operand denotations.
+ * is the intersection (using `&` or `safe_&` if `safeIntersection` is true)
+ * of the infos of the operand denotations.
*
* If SingleDenotations with different signatures are joined, return NoDenotation.
*/
- def & (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = {
+ def & (that: Denotation, pre: Type, safeIntersection: Boolean = false)(implicit ctx: Context): Denotation = {
/** Try to merge denot1 and denot2 without adding a new signature. */
def mergeDenot(denot1: Denotation, denot2: SingleDenotation): Denotation = denot1 match {
@@ -317,7 +318,11 @@ object Denotations {
else if (preferSym(sym2, sym1)) sym2
else sym1
val jointInfo =
- try info1 & info2
+ try
+ if (safeIntersection)
+ info1 safe_& info2
+ else
+ info1 & info2
catch {
case ex: MergeError =>
if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 43276d254..cc81b9558 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -459,7 +459,7 @@ object Types {
val jointInfo =
if (rinfo.isAlias) rinfo
else if (pdenot.info.isAlias) pdenot.info
- else if (ctx.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo)
+ else if (ctx.pendingMemberSearches.contains(name)) pdenot.info safe_& rinfo
else
try pdenot.info & rinfo
catch {
@@ -470,11 +470,15 @@ object Types {
// the special shortcut for Any in derivesFrom was as yet absent. To reproduce,
// remove the special treatment of Any in derivesFrom and compile
// sets.scala.
- safeAnd(pdenot.info, rinfo)
+ pdenot.info safe_& rinfo
}
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
- } else
- pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre)
+ } else {
+ pdenot & (
+ new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)),
+ pre,
+ safeIntersection = ctx.pendingMemberSearches.contains(name))
+ }
}
def goThis(tp: ThisType) = {
val d = go(tp.underlying)
@@ -501,12 +505,10 @@ object Types {
go(next)
}
}
- def goAnd(l: Type, r: Type) = go(l) & (go(r), pre)
- def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
- 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
+ def goAnd(l: Type, r: Type) = {
+ go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name))
}
+ def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
{ val recCount = ctx.findMemberCount + 1
ctx.findMemberCount = recCount
@@ -705,6 +707,19 @@ object Types {
ctx.typeComparer.glb(this, that)
}
+ /** Safer version of `&`.
+ *
+ * This version does not simplify the upper bound of the intersection of
+ * two TypeBounds. The simplification done by `&` requires subtyping checks
+ * which may end up calling `&` again, in most cases this should be safe
+ * but because of F-bounded types, this can result in an infinite loop
+ * (which will be masked unless `-Yno-deep-subtypes` is enabled).
+ */
+ def safe_& (that: Type)(implicit ctx: Context): Type = (this, that) match {
+ case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2))
+ case _ => this & that
+ }
+
def | (that: Type)(implicit ctx: Context): Type = track("|") {
ctx.typeComparer.lub(this, that)
}
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index bc212bdca..029387333 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -186,7 +186,7 @@ class tests extends CompilerTest {
.filter(_.nonEmpty)
.toList
- @Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: scala2mode)(allowDeepSubtypes)
+ @Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: scala2mode)
@Test def compileMixed = compileLine(
"""tests/pos/B.scala
|./scala-scala/src/library/scala/collection/immutable/Seq.scala