Skip to content

Fix #1795: Avoid infinite recursion between member and asSeenFrom #1848

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

Merged
merged 2 commits into from
Dec 21, 2016
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
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ object Config {
*/
final val LogPendingFindMemberThreshold = 10

/** Maximal number of outstanding recursive calls to findMember */
/** Maximal number of outstanding recursive calls to findMember before backing out
* when findMemberLimit is set.
*/
final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4
}
20 changes: 16 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import NameOps._
import Decorators._
import StdNames._
import Annotations._
import util.SimpleMap
import config.Config
import util.{SimpleMap, Property}
import collection.mutable
import ast.tpd._

Expand Down Expand Up @@ -67,7 +68,10 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) {
if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre)) {
ctx.base.unsafeNonvariant = ctx.runId
AnnotatedType(pre, Annotation(defn.UnsafeNonvariantAnnot, Nil))
pre match {
case AnnotatedType(_, ann) if ann.symbol == defn.UnsafeNonvariantAnnot => pre
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it's related. Before we were running into the member/asSeenFrom mutual recursion we stackoverflowed here. Avoiding this by not piling up the same annotation makes sense independently.

case _ => AnnotatedType(pre, Annotation(defn.UnsafeNonvariantAnnot, Nil))
}
}
else pre
}
Expand All @@ -85,13 +89,15 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
if (sym.isStatic) tp
else {
val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap)
if (pre1.isUnsafeNonvariant)
pre1.member(tp.name).info match {
if (pre1.isUnsafeNonvariant) {
val safeCtx = ctx.withProperty(TypeOps.findMemberLimit, Some(()))
pre1.member(tp.name)(safeCtx).info match {
case TypeAlias(alias) =>
// try to follow aliases of this will avoid skolemization.
return alias
case _ =>
}
}
tp.derivedSelect(pre1)
}
case tp: ThisType =>
Expand Down Expand Up @@ -554,4 +560,10 @@ trait TypeOps { this: Context => // TODO: Make standalone object.

object TypeOps {
@sharable var track = false // !!!DEBUG

/** When a property with this key is set in a context, it limit the number
* of recursive member searches. If the limit is reached, findMember returns
* NoDenotation.
*/
val findMemberLimit = new Property.Key[Unit]
}
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,12 @@ object Types {

{ val recCount = ctx.findMemberCount + 1
ctx.findMemberCount = recCount
if (recCount >= Config.LogPendingFindMemberThreshold)
if (recCount >= Config.LogPendingFindMemberThreshold) {
ctx.pendingMemberSearches = name :: ctx.pendingMemberSearches
if (ctx.property(TypeOps.findMemberLimit).isDefined &&
ctx.findMemberCount > Config.PendingFindMemberLimit)
return NoDenotation
}
}

//assert(ctx.findMemberCount < 20)
Expand Down
13 changes: 13 additions & 0 deletions tests/pos/i1795.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
sealed trait T1 {type M1}

case object o1 extends T1

sealed trait T2 {type M2}

case object o2 extends T2

class TestX {
type TestT1 <: T1 {type M1 = TestT2}
type TestT2 <: T2 {type M2 = TestT1}
//val x: TestT1 = o1
}