diff --git a/project/Build.scala b/project/Build.scala index c8134954c6bf..b0bad8f4ca20 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -6,7 +6,9 @@ import scala.reflect.io.Path object DottyBuild extends Build { - val travisMemLimit = List("-Xmx1g", "-Xss2m") + // Currently, this cannot be increased without hitting the maximum amount of memory + // available on the Jenkins VMs + val travisMemLimit = List("-Xmx1100m") val JENKINS_BUILD = "dotty.jenkins.build" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 8016c57f3e6c..d384bd5c4951 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1802,6 +1802,14 @@ object SymDenotations { def withModuleClass(moduleClassFn: Context => Symbol): this.type = { myModuleClassFn = moduleClassFn; this } } + /** A subclass of LazyTypes where type parameters can be completed independently of + * the info. + */ + abstract class TypeParamsCompleter extends LazyType { + /** The type parameters computed by the completer before completion has finished */ + def completerTypeParams(sym: Symbol): List[TypeSymbol] + } + val NoSymbolFn = (ctx: Context) => NoSymbol /** A missing completer */ diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index aab327ce9a21..e09328205028 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -4,6 +4,7 @@ package core import Types._ import Contexts._ import Symbols._ +import SymDenotations.TypeParamsCompleter import Decorators._ import util.Stats._ import util.common._ @@ -240,15 +241,21 @@ class TypeApplications(val self: Type) extends AnyVal { case self: TypeRef => val tsym = self.symbol if (tsym.isClass) tsym.typeParams - else if (tsym.isAliasType) self.underlying.typeParams - else if (tsym.isCompleting) - // We are facing a problem when computing the type parameters of an uncompleted - // abstract type. We can't access the bounds of the symbol yet because that - // would cause a cause a cyclic reference. So we return `Nil` instead - // and try to make up for it later. The acrobatics in Scala2Unpicker#readType - // for reading a TypeRef show what's neeed. - Nil - else tsym.info.typeParams + else tsym.infoOrCompleter match { + case completer: TypeParamsCompleter => + val tparams = completer.completerTypeParams(tsym) + if (tsym.isClass) tparams + else defn.LambdaTrait(tparams.map(_.variance)).typeParams + case _ => + if (!tsym.isCompleting || tsym.isAliasType) tsym.info.typeParams + else + // We are facing a problem when computing the type parameters of an uncompleted + // abstract type. We can't access the bounds of the symbol yet because that + // would cause a cause a cyclic reference. So we return `Nil` instead + // and try to make up for it later. The acrobatics in Scala2Unpicker#readType + // for reading a TypeRef show what's needed. + Nil + } case self: RefinedType => // inlined and optimized version of // val sym = self.LambdaTrait diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 04cd2249dbae..6896468ba884 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -35,7 +35,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. * Instead we produce an annotated type that marks the prefix as unsafe: * * (x: (C @ UnsafeNonvariant)#T)C#T - + * We also set a global state flag `unsafeNonvariant` to the current run. * When typing a Select node, typer will check that flag, and if it * points to the current run will scan the result type of the select for diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 45897dd643ad..84602ccf7649 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2506,11 +2506,11 @@ object Types { // ----- Skolem types ----------------------------------------------- /** A skolem type reference with underlying type `binder`. */ - abstract case class SkolemType(info: Type) extends CachedProxyType with ValueType with SingletonType { + abstract case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType { override def underlying(implicit ctx: Context) = info def derivedSkolemType(info: Type)(implicit ctx: Context) = if (info eq this.info) this else SkolemType(info) - override def computeHash: Int = identityHash + override def hashCode: Int = identityHash override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] override def toString = s"Skolem($info)" } diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 57032c4d9ecb..6ded7c10959a 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -138,9 +138,7 @@ object Checking { case SuperType(thistp, _) => isInteresting(thistp) case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2) case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2) - case _: RefinedType => false - // Note: it's important not to visit parents of RefinedTypes, - // since otherwise spurious #Apply projections might be inserted. + case _: RefinedType => true case _ => false } // If prefix is interesting, check info of typeref recursively, marking the referred symbol @@ -148,10 +146,12 @@ object Checking { // is hit again. Without this precaution we could stackoverflow here. if (isInteresting(pre)) { val info = tp.info - val symInfo = tp.symbol.info - if (tp.symbol.exists) tp.symbol.info = SymDenotations.NoCompleter + val sym = tp.symbol + if (sym.infoOrCompleter == SymDenotations.NoCompleter) throw CyclicReference(sym) + val symInfo = sym.info + if (sym.exists) sym.info = SymDenotations.NoCompleter try checkInfo(info) - finally if (tp.symbol.exists) tp.symbol.info = symInfo + finally if (sym.exists) sym.info = symInfo } tp } catch { diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index b24916be8005..406e7378f218 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -478,10 +478,31 @@ class Namer { typer: Typer => } /** The completer of a symbol defined by a member def or import (except ClassSymbols) */ - class Completer(val original: Tree)(implicit ctx: Context) extends LazyType { + class Completer(val original: Tree)(implicit ctx: Context) extends TypeParamsCompleter { protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original) + private var myTypeParams: List[TypeSymbol] = null + private var nestedCtx: Context = null + + def completerTypeParams(sym: Symbol): List[TypeSymbol] = { + if (myTypeParams == null) { + //println(i"completing type params of $sym in ${sym.owner}") + myTypeParams = original match { + case tdef: TypeDef => + nestedCtx = localContext(sym).setNewScope + locally { + implicit val ctx: Context = nestedCtx + completeParams(tdef.tparams) + tdef.tparams.map(symbolOfTree(_).asType) + } + case _ => + Nil + } + } + myTypeParams + } + private def typeSig(sym: Symbol): Type = original match { case original: ValDef => if (sym is Module) moduleValSig(sym) @@ -492,7 +513,7 @@ class Namer { typer: Typer => typer1.defDefSig(original, sym)(localContext(sym).setTyper(typer1)) case original: TypeDef => assert(!original.isClassDef) - typeDefSig(original, sym)(localContext(sym).setNewScope) + typeDefSig(original, sym, completerTypeParams(sym))(nestedCtx) case imp: Import => try { val expr1 = typedAheadExpr(imp.expr, AnySelectionProto) @@ -840,9 +861,7 @@ class Namer { typer: Typer => else valOrDefDefSig(ddef, sym, typeParams, paramSymss, wrapMethType) } - def typeDefSig(tdef: TypeDef, sym: Symbol)(implicit ctx: Context): Type = { - completeParams(tdef.tparams) - val tparamSyms = tdef.tparams map symbolOfTree + def typeDefSig(tdef: TypeDef, sym: Symbol, tparamSyms: List[TypeSymbol])(implicit ctx: Context): Type = { val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree] //val toParameterize = tparamSyms.nonEmpty && !isDerived //val needsLambda = sym.allOverriddenSymbols.exists(_ is HigherKinded) && !isDerived diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index cb3bb0f205e9..511a12ea7c46 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -882,7 +882,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit else (arg, WildcardType) val arg1 = typed(desugaredArg, argPt) - adaptTypeArg(arg1, if (tparam.isCompleted) tparam.info else WildcardType) + adaptTypeArg(arg1, tparam.info) } val args1 = args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] // check that arguments conform to bounds is done in phase PostTyper diff --git a/test/dotc/scala-collections.whitelist b/test/dotc/scala-collections.whitelist index 2abb16c1ec20..f6a13cc72926 100644 --- a/test/dotc/scala-collections.whitelist +++ b/test/dotc/scala-collections.whitelist @@ -266,7 +266,7 @@ ./scala-scala/src/library/scala/collection/generic/ParFactory.scala # https://github.com/lampepfl/dotty/issues/974 -> @smarter -#./scala-scala/src/library/scala/collection/generic/MutableSortedSetFactory.scala +./scala-scala/src/library/scala/collection/generic/MutableSortedSetFactory.scala # cyclic reference, maybe related to #974 -> @smarter #./scala-scala/src/library/scala/collection/generic/ParSetFactory.scala diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 2810a8b551e9..354bc62d20b5 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -158,6 +158,7 @@ class tests extends CompilerTest { @Test def neg_finalSealed = compileFile(negDir, "final-sealed", xerrors = 2) @Test def neg_i705 = compileFile(negDir, "i705-inner-value-class", xerrors = 7) @Test def neg_i866 = compileFile(negDir, "i866", xerrors = 2) + @Test def neg_i974 = compileFile(negDir, "i974", xerrors = 2) @Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4) @Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2) @Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8) @@ -185,6 +186,15 @@ class tests extends CompilerTest { .toList @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 + |./scala-scala/src/library/scala/package.scala + |./scala-scala/src/library/scala/collection/GenSeqLike.scala + |./scala-scala/src/library/scala/collection/SeqLike.scala + |./scala-scala/src/library/scala/collection/generic/GenSeqFactory.scala""".stripMargin) + @Test def compileIndexedSeq = compileLine("./scala-scala/src/library/scala/collection/immutable/IndexedSeq.scala") + @Test def dotty = compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant"))(allowDeepSubtypes) // note the -deep argument @Test def dotc_ast = compileDir(dotcDir, "ast") diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala index 09b608f22e1a..c65710e7d718 100644 --- a/test/test/CompilerTest.scala +++ b/test/test/CompilerTest.scala @@ -221,8 +221,8 @@ abstract class CompilerTest extends DottyTest { case ExistsSame => // nothing else to do case ExistsDifferent => val nextDest = dest.parent / (dest match { - case f: SFile => SFile(replaceVersion(f.stripExtension, nr)).addExtension(f.extension) case d: Directory => Directory(replaceVersion(d.name, nr)) + case f => SFile(replaceVersion(f.stripExtension, nr)).addExtension(f.extension) }) computeDestAndCopyFiles(source, nextDest, kind, flags, nerr, nr + 1, partestOutput) } diff --git a/tests/neg/i974.scala b/tests/neg/i974.scala new file mode 100644 index 000000000000..89db4b2d9339 --- /dev/null +++ b/tests/neg/i974.scala @@ -0,0 +1,8 @@ +trait Foo[T <: Bar[T]#Elem] // error: illegal cyclic reference +trait Bar[T] { + type Elem = T +} +trait Foo2[T <: Bar2[T]#Elem] // error: illegal cyclic reference +trait Bar2[T] { + type Elem = T +} diff --git a/tests/pos/B.scala b/tests/pos/B.scala new file mode 100644 index 000000000000..7e4f26e7de88 --- /dev/null +++ b/tests/pos/B.scala @@ -0,0 +1,6 @@ +object B{ + def main(args: Array[String]): Unit = { + val s = List(1,2,3) + () + } +} diff --git a/tests/pos/i974.scala b/tests/pos/i974.scala new file mode 100644 index 000000000000..4c7c15e8d856 --- /dev/null +++ b/tests/pos/i974.scala @@ -0,0 +1,2 @@ +class Foo[A] +class Bar[CC[X] <: Foo[CC[X]]]