Skip to content

Fix 10256: Avoid crashing in LazyRef.ref when checking for cycles #10270

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 1 commit into from
Nov 20, 2020
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
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ object Mode {
*/
val TypevarsMissContext: Mode = newMode(4, "TypevarsMissContext")

/** Are we looking for cyclic references? */
val CheckCyclic: Mode = newMode(5, "CheckCyclic")

/** We are in a pattern alternative */
Expand Down
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2680,7 +2680,7 @@ object Types {
}
}

case class LazyRef(private var refFn: Context ?=> Type, reportCycles: Boolean = false) extends UncachedProxyType with ValueType {
case class LazyRef(private var refFn: Context ?=> Type) extends UncachedProxyType with ValueType {
private var myRef: Type = null
private var computed = false

Expand All @@ -2689,7 +2689,7 @@ object Types {
if myRef == null then
// if errors were reported previously handle this by throwing a CyclicReference
// instead of crashing immediately. A test case is neg/i6057.scala.
assert(reportCycles || ctx.reporter.errorsReported)
assert(ctx.mode.is(Mode.CheckCyclic) || ctx.reporter.errorsReported)
throw CyclicReference(NoDenotation)
else
computed = true
Expand Down Expand Up @@ -4515,7 +4515,9 @@ object Types {
else defn.AnyType // dummy type in case of errors
def refineSelfType(selfType: Type) =
RefinedType(selfType, sym.name,
TypeAlias(LazyRef(force, reportCycles = true)))
TypeAlias(
withMode(Mode.CheckCyclic)(
LazyRef(force))))
cinfo.selfInfo match
case self: Type =>
cinfo.derivedClassInfo(
Expand Down
24 changes: 11 additions & 13 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -406,21 +406,19 @@ object Checking {
for (parent <- parents; mbr <- parent.abstractTypeMembers if qualifies(mbr.symbol))
yield mbr.name.asTypeName

for (name <- abstractTypeNames)
try {
val mbr = joint.member(name)
mbr.info match {
case bounds: TypeBounds =>
!checkNonCyclic(mbr.symbol, bounds, reportErrors = true).isError
case _ =>
true
}
}
catch {
case ex: RecursionOverflow =>
withMode(Mode.CheckCyclic) {
for name <- abstractTypeNames do
try
val mbr = joint.member(name)
mbr.info match
case bounds: TypeBounds =>
!checkNonCyclic(mbr.symbol, bounds, reportErrors = true).isError
case _ =>
true
catch case _: RecursionOverflow | _: CyclicReference =>
report.error(em"cyclic reference involving type $name", pos)
false
}
}
}

/** Check that symbol's definition is well-formed. */
Expand Down
7 changes: 7 additions & 0 deletions tests/neg/i10256.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
trait Foo[T <: Foo[T]] {
type I <: Foo[I]
}

trait Bar[T <: Foo[T]] extends Foo[T] { // error: cyclic
self: T =>
}