Skip to content

Commit 2ee486d

Browse files
committed
Avoid infinite subtyping checks when intersecting denotations
This allows us to run compileStdLib without deep subtypes again.
1 parent f3bf7c1 commit 2ee486d

File tree

3 files changed

+33
-13
lines changed

3 files changed

+33
-13
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,12 @@ object Denotations {
259259
*
260260
* If there is no preferred accessible denotation, return a JointRefDenotation
261261
* with one of the operand symbols (unspecified which one), and an info which
262-
* is intersection (&) of the infos of the operand denotations.
262+
* is the intersection (using `&` or `safe_&` if `safeIntersection` is true)
263+
* of the infos of the operand denotations.
263264
*
264265
* If SingleDenotations with different signatures are joined, return NoDenotation.
265266
*/
266-
def & (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = {
267+
def & (that: Denotation, pre: Type, safeIntersection: Boolean = false)(implicit ctx: Context): Denotation = {
267268

268269
/** Try to merge denot1 and denot2 without adding a new signature. */
269270
def mergeDenot(denot1: Denotation, denot2: SingleDenotation): Denotation = denot1 match {
@@ -317,7 +318,11 @@ object Denotations {
317318
else if (preferSym(sym2, sym1)) sym2
318319
else sym1
319320
val jointInfo =
320-
try info1 & info2
321+
try
322+
if (safeIntersection)
323+
info1 safe_& info2
324+
else
325+
info1 & info2
321326
catch {
322327
case ex: MergeError =>
323328
if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)

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

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ object Types {
459459
val jointInfo =
460460
if (rinfo.isAlias) rinfo
461461
else if (pdenot.info.isAlias) pdenot.info
462-
else if (ctx.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo)
462+
else if (ctx.pendingMemberSearches.contains(name)) pdenot.info safe_& rinfo
463463
else
464464
try pdenot.info & rinfo
465465
catch {
@@ -470,11 +470,15 @@ object Types {
470470
// the special shortcut for Any in derivesFrom was as yet absent. To reproduce,
471471
// remove the special treatment of Any in derivesFrom and compile
472472
// sets.scala.
473-
safeAnd(pdenot.info, rinfo)
473+
pdenot.info safe_& rinfo
474474
}
475475
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
476-
} else
477-
pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre)
476+
} else {
477+
pdenot & (
478+
new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)),
479+
pre,
480+
safeIntersection = ctx.pendingMemberSearches.contains(name))
481+
}
478482
}
479483
def goThis(tp: ThisType) = {
480484
val d = go(tp.underlying)
@@ -501,12 +505,10 @@ object Types {
501505
go(next)
502506
}
503507
}
504-
def goAnd(l: Type, r: Type) = go(l) & (go(r), pre)
505-
def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
506-
def safeAnd(tp1: Type, tp2: Type): Type = (tp1, tp2) match {
507-
case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2))
508-
case _ => tp1 & tp2
508+
def goAnd(l: Type, r: Type) = {
509+
go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name))
509510
}
511+
def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
510512

511513
{ val recCount = ctx.findMemberCount + 1
512514
ctx.findMemberCount = recCount
@@ -705,6 +707,19 @@ object Types {
705707
ctx.typeComparer.glb(this, that)
706708
}
707709

710+
/** Safer version of `&`.
711+
*
712+
* This version does not simplify the upper bound of the intersection of
713+
* two TypeBounds. The simplification done by `&` requires subtyping checks
714+
* which may end up calling `&` again, in most cases this should be safe
715+
* but because of F-bounded types, this can result in an infinite loop
716+
* (which will be masked unless `-Yno-deep-subtypes` is enabled).
717+
*/
718+
def safe_& (that: Type)(implicit ctx: Context): Type = (this, that) match {
719+
case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2))
720+
case _ => this & that
721+
}
722+
708723
def | (that: Type)(implicit ctx: Context): Type = track("|") {
709724
ctx.typeComparer.lub(this, that)
710725
}

test/dotc/tests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class tests extends CompilerTest {
185185
.filter(_.nonEmpty)
186186
.toList
187187

188-
@Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: scala2mode)(allowDeepSubtypes)
188+
@Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: scala2mode)
189189
@Test def dotty = compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant"))(allowDeepSubtypes) // note the -deep argument
190190

191191
@Test def dotc_ast = compileDir(dotcDir, "ast")

0 commit comments

Comments
 (0)