Skip to content

Change early typeparams2 #1028

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

Closed
wants to merge 7 commits into from
Closed
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
2 changes: 1 addition & 1 deletion scripts/common
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ update() {

export LC_ALL=en_US.UTF-8

sbtArgs="-Ddotty.jenkins.build=yes -Dfile.encoding=UTF-8 -ivy $baseDir/ivy2 -Dsbt.global.base=$HOME/.sbt/0.13 -sbt-dir $HOME/.sbt/0.13"
sbtArgs="-Ddotty.jenkins.build=yes -J-Xmx2G -Dfile.encoding=UTF-8 -ivy $baseDir/ivy2 -Dsbt.global.base=$HOME/.sbt/0.13 -sbt-dir $HOME/.sbt/0.13"
8 changes: 8 additions & 0 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
25 changes: 16 additions & 9 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package core
import Types._
import Contexts._
import Symbols._
import SymDenotations.TypeParamsCompleter
import Decorators._
import util.Stats._
import util.common._
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

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

I think this will always be false since we check if (tsym.isClass) above and go in another branch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is fixed in a subsequent commit.

Copy link
Member

Choose a reason for hiding this comment

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

In which subsequent commit? I'm commenting on the whole diff here and I still see:

if (tsym.isClass) ...
else tsym.infoOrCompleter match {
  case ... =>
    val tparams = ...
    if (tsym.isClass) ...

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
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 31 additions & 3 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,35 @@ object Types {
NoType
}

loop(this)
/** Reduce C[hki = Argi] # Apply where C is a non-lambda class
* to C[Argi], renaming references as necessary */
def betaReduceCls(cls: Symbol, tp: Type): Type = tp.stripTypeVar match {
case tp @ RefinedType(parent, rname) =>
val parent1 = betaReduceCls(cls, parent)
def paramName(hkArgName: TypeName): TypeName =
cls.typeParams.apply(rname.hkArgIndex).name
def mapArg(rt: RefinedType) = new TypeMap {
def apply(t: Type): Type = t match {
case TypeRef(RefinedThis(`tp`), name) if name.isHkArgName =>
TypeRef(RefinedThis(rt), paramName(name))
case _ =>
mapOver(t)
}
}
if (rname.isHkArgName)
RefinedType(parent1, paramName(rname.asTypeName), mapArg(_)(tp.refinedInfo))
else parent1
case _ =>
tp
}

if (name == tpnme.hkApply) {
val cref = underlyingClassRef(refinementOK = true)
val cls = cref.typeSymbol
if (cref.isEtaExpandable && !defn.isBottomClass(cls)) betaReduceCls(cls, this)
else loop(this)
}
else loop(this)
}

/** The type <this . name> , reduced if possible */
Expand Down Expand Up @@ -2506,11 +2534,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)"
}
Expand Down
12 changes: 6 additions & 6 deletions src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,20 @@ 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
// with NoCompleter. This provokes a CyclicReference when the symbol
// 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 {
Expand Down
29 changes: 24 additions & 5 deletions src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/dotc/scala-collections.whitelist
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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")
Expand Down
4 changes: 3 additions & 1 deletion test/dotty/partest/DPDirectCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ class DPDirectCompiler(runner: DPTestRunner) extends nest.DirectCompiler(runner)
}
} catch {
case t: Throwable =>
t.printStackTrace
if (t.isInstanceOf[java.lang.OutOfMemoryError])
println(s"total memory = "+ Runtime.getRuntime().totalMemory / (1024*1024) +"mb")
t.printStackTrace()
t.printStackTrace(clogWriter)
runner.genCrash(t)
} finally {
Expand Down
2 changes: 1 addition & 1 deletion test/test/CompilerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 8 additions & 0 deletions tests/neg/i974.scala
Original file line number Diff line number Diff line change
@@ -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
}
6 changes: 6 additions & 0 deletions tests/pos/B.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
object B{
def main(args: Array[String]): Unit = {
val s = List(1,2,3)
()
}
}
2 changes: 2 additions & 0 deletions tests/pos/i974.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class Foo[A]
class Bar[CC[X] <: Foo[CC[X]]]