From 9c38a61d4b3319999a43265ea17f85a1b3ca6886 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 5 Feb 2016 16:53:12 +0100 Subject: [PATCH 1/3] Make LazyRef#ref a def instead of a lazy val It's slightly more efficient. There was no need to have a separate cache for the lazy val. --- src/dotty/tools/dotc/core/Types.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index e266bab6f482..d76f57ba6f30 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1972,7 +1972,7 @@ object Types { case class LazyRef(refFn: () => Type) extends UncachedProxyType with ValueType { private var myRef: Type = null private var computed = false - lazy val ref = { + def ref = { if (computed) assert(myRef != null) else { computed = true @@ -1980,6 +1980,7 @@ object Types { } myRef } + def evaluating = computed && myRef == null override def underlying(implicit ctx: Context) = ref override def toString = s"LazyRef($ref)" override def equals(other: Any) = other match { From 041d42f58eae860f88d6f1ea54305c1a7dca6c42 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 5 Feb 2016 16:56:38 +0100 Subject: [PATCH 2/3] Handle subtyping of LazyVals that are in train of being evaluated. Instead of forcing again, and causing an assertion error, back out assuming that the result is false. Fixes first problem with #859. --- src/dotty/tools/dotc/core/TypeComparer.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index f468a573f7ca..592f2c5de725 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -199,7 +199,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } compareWild case tp2: LazyRef => - isSubType(tp1, tp2.ref) + !tp2.evaluating && isSubType(tp1, tp2.ref) case tp2: AnnotatedType => isSubType(tp1, tp2.tpe) // todo: refine? case tp2: ThisType => @@ -299,7 +299,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } compareWild case tp1: LazyRef => - isSubType(tp1.ref, tp2) + // If `tp1` is in train of being evaluated, don't force it + // because that would cause an assertionError. Return false instead. + // See i859.scala for an example where we hit this case. + !tp1.evaluating && isSubType(tp1.ref, tp2) case tp1: AnnotatedType => isSubType(tp1.tpe, tp2) case AndType(tp11, tp12) => From c7e71b81f0bf4ccf1f2d442e0425fe6f0dfe2c99 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 5 Feb 2016 16:58:14 +0100 Subject: [PATCH 3/3] Surive non-existing sourceModule in Scala2 pickled info. It seems when unpickling nsc that some module classes come without a source module. Survive this situation rather than crashing. i859.scala is an example. i859 compiles with the patch, but causes a deep subtype when unpickling. Not sure whether scalac does the same. --- .../tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 6 ++++-- test/dotc/tests.scala | 1 + tests/pos-special/i859.scala | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 tests/pos-special/i859.scala diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 618e3ceeac43..abf31a006abb 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -101,10 +101,12 @@ object Scala2Unpickler { case cinfo => (Nil, cinfo) } val ost = - if ((selfInfo eq NoType) && (denot is ModuleClass)) + if ((selfInfo eq NoType) && (denot is ModuleClass) && denot.sourceModule.exists) + // it seems sometimes the source module does not exist for a module class. + // An example is `scala.reflect.internal.Trees.Template$. Without the + // `denot.sourceModule.exists` provision i859.scala crashes in the backend. denot.owner.thisType select denot.sourceModule else selfInfo - denot.info = ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, decls, ost) // first rough info to avoid CyclicReferences var parentRefs = ctx.normalizeToClassRefs(parents, cls, decls) if (parentRefs.isEmpty) parentRefs = defn.ObjectType :: Nil diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 2944d69e90c2..16917742d811 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -103,6 +103,7 @@ class tests extends CompilerTest { @Test def pos_i871 = compileFile(posSpecialDir, "i871", scala2mode) @Test def pos_variancesConstr = compileFile(posSpecialDir, "variances-constr", scala2mode) + @Test def pos_859 = compileFile(posSpecialDir, "i859", scala2mode)(allowDeepSubtypes) @Test def new_all = compileFiles(newDir, twice) diff --git a/tests/pos-special/i859.scala b/tests/pos-special/i859.scala new file mode 100644 index 000000000000..a9f6b51c9c4b --- /dev/null +++ b/tests/pos-special/i859.scala @@ -0,0 +1,3 @@ +class Analyzer { + def foo: scala.tools.nsc.Global = ??? +}