From 69ed9b778227f171eb643a9cd187d4e66a52d2f7 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 27 Nov 2015 04:54:20 +0100 Subject: [PATCH] [PROOF-OF-CONCEPT] Avoid forcing type aliases to get their typeParams This is a strawman proposal for fixing #970, before this commit, the following fails with a cyclic reference error: dotc 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 where B.scala is defined as: object B{ def main(args: Array[String]): Unit = { val s = List(1,2,3) () } } Here's what I think is happening: 1. Unpickling a Scala2 TypeRef requires us to perform type application. (`Scala2Unpickler#readType`) 2. To apply a TypeAlias to some arguments, we first need to determine its type parameters. (`TypeApplications#appliedTo` and `TypeApplications#typeParams`) 3. To do this, we look at the type parameters of the underlying type of the TypeAlias, this requires completing the TypeAlias. 4. If the TypeAlias is defined in a source tree and not unpickled, this forces us to typecheck its right-hand side. (`Namer#Completer#typeSig`) 5. In turns, this forces various classes to be completed, which might themselves refer indirectly to type aliases, forcing even more stuff. This commit is a hacky way to avoid 3. by only completing the type parameters of a type alias instead of completing the whole type alias when we don't need it. Let me know if you think the basic idea is sound or not and if you can think of a nicer way to implement it! --- .../tools/dotc/core/TypeApplications.scala | 22 +++++++++++++--- src/dotty/tools/dotc/typer/Namer.scala | 26 ++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 661975dabce9..40cbb92e7786 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -16,6 +16,8 @@ import util.Positions.Position import config.Printers._ import collection.mutable import java.util.NoSuchElementException +import ast.untpd.TypeDef +import typer.Namer object TypeApplications { @@ -63,7 +65,14 @@ class TypeApplications(val self: Type) extends AnyVal { case self: TypeRef => val tsym = self.typeSymbol if (tsym.isClass) tsym.typeParams - else if (tsym.isAliasType) self.underlying.typeParams + else if (tsym.isAliasType) { + tsym.infoOrCompleter match { + case c: Namer#Completer => + c.typeParams(tsym) + case _ => + self.underlying.typeParams + } + } else { val lam = LambdaClass(forcing = false) if (lam.exists) lam.typeParams else Nil @@ -200,6 +209,13 @@ class TypeApplications(val self: Type) extends AnyVal { case nil => tp } + def canForce(sym: Symbol) = sym.infoOrCompleter match { + case c: Namer#Completer => + !sym.isAliasType + case _ => + true + } + /** Instantiate type `tp` with `args`. * @param original The original type for which we compute the type parameters * This makes a difference for refinement types, because @@ -209,7 +225,7 @@ class TypeApplications(val self: Type) extends AnyVal { def instantiate(tp: Type, original: Type): Type = tp match { case tp: TypeRef => val tsym = tp.symbol - if (tsym.isAliasType) tp.underlying.appliedTo(args) + if (tsym.isAliasType && canForce(tsym)) tp.underlying.appliedTo(args) else { val safeTypeParams = if (tsym.isClass || !tp.typeSymbol.isCompleting) original.typeParams @@ -242,7 +258,7 @@ class TypeApplications(val self: Type) extends AnyVal { case tp: TypeRef => val sym = tp.symbol if (sym.isClass) sym.isLambdaTrait - else !sym.isAliasType || isKnownHK(tp.info) + else !sym.isAliasType || !canForce(sym) || isKnownHK(tp.info) case tp: TypeProxy => isKnownHK(tp.underlying) case _ => false } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 5eebdbad1cc8..491b7fbe6238 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -478,6 +478,8 @@ 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 { + var completedParams: Boolean = false + var nestedCtx: Context = null protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original) @@ -491,7 +493,9 @@ class Namer { typer: Typer => typer1.defDefSig(original, sym)(localContext(sym).setTyper(typer1)) case original: TypeDef => assert(!original.isClassDef) - typeDefSig(original, sym)(localContext(sym).setNewScope) + if (nestedCtx == null) + nestedCtx = localContext(sym).setNewScope + typeDefSig(original, sym, completedParams)(nestedCtx) case imp: Import => try { val expr1 = typedAheadExpr(imp.expr, AnySelectionProto) @@ -503,6 +507,21 @@ class Namer { typer: Typer => } } + def typeParams(sym: Symbol) = original match { + case tdef: TypeDef => + if (nestedCtx == null) + nestedCtx = localContext(sym).setNewScope + + { + implicit val ctx: Context = nestedCtx + if (!completedParams) { + completeParams(tdef.tparams) + completedParams = true + } + tdef.tparams map (symbolOfTree(_).asType) + } + } + final override def complete(denot: SymDenotation)(implicit ctx: Context) = { if (completions != noPrinter && ctx.typerState != this.ctx.typerState) { completions.println(completions.getClass.toString) @@ -807,8 +826,9 @@ class Namer { typer: Typer => else valOrDefDefSig(ddef, sym, typeParams, paramSymss, wrapMethType) } - def typeDefSig(tdef: TypeDef, sym: Symbol)(implicit ctx: Context): Type = { - completeParams(tdef.tparams) + def typeDefSig(tdef: TypeDef, sym: Symbol, completedParams: Boolean)(implicit ctx: Context): Type = { + if (!completedParams) + completeParams(tdef.tparams) val tparamSyms = tdef.tparams map symbolOfTree val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree] //val toParameterize = tparamSyms.nonEmpty && !isDerived