From da53cc62ba6f833b25522ca29428cec7349d11a1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 11 Oct 2014 11:08:25 +0200 Subject: [PATCH 01/31] Avoid creating ConstantTypes by TypeAssigner after erasure. --- src/dotty/tools/dotc/transform/Erasure.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index aae1ff1baf79..2505a50b7a77 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -238,7 +238,7 @@ object Erasure extends TypeTestsCasts{ override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = { assert(tree.hasType) - val erased = erasedType(tree)(ctx.withPhase(ctx.erasurePhase)) + val erased = erasedType(tree) ctx.log(s"promoting ${tree.show}: ${erased.showWithUnderlying()}") tree.withType(erased) } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index c23b820e4a57..6b0a8bf9e34a 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -209,10 +209,11 @@ trait TypeAssigner { def assignType(tree: untpd.Literal)(implicit ctx: Context) = tree.withType { - tree.const.tag match { + val value = tree.const + value.tag match { case UnitTag => defn.UnitType case NullTag => defn.NullType - case _ => ConstantType(tree.const) + case _ => if (ctx.erasedTypes) value.tpe else ConstantType(value) } } From 398b1daaa5e511822a776e7c0312eb2df3b8c5cf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 11 Oct 2014 19:29:13 +0200 Subject: [PATCH 02/31] Add possibility to compile all files in a directory and all subdirectories. We better make this configurable. Because sometimes we want to compile only the files in the immediate directory. --- test/dotc/tests.scala | 2 +- test/test/CompilerTest.scala | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 02184599729a..ceadacaef9fd 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -119,7 +119,7 @@ class tests extends CompilerTest { @Test def dotc_util = compileDir(dotcDir + "tools/dotc/util", twice) @Test def tools_io = compileDir(dotcDir + "tools/io", twice) - @Test def tools = compileDir(dotcDir + "tools", twice)(allowDeepSubtypes) + @Test def tools = compileDir(dotcDir + "tools", "-deep" :: twice)(allowDeepSubtypes) @Test def testNonCyclic = compileArgs(Array( dotcDir + "tools/dotc/CompilationUnit.scala", diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala index f5b13845434c..c9c7c602bdea 100644 --- a/test/test/CompilerTest.scala +++ b/test/test/CompilerTest.scala @@ -24,8 +24,12 @@ class CompilerTest extends DottyTest { compileDir(Directory(path), args, xerrors) def compileDir(dir: Directory, args: List[String], xerrors: Int)(implicit defaultOptions: List[String]): Unit = { - val fileNames = dir.deepFiles.toArray.map(_.toString).filter(_ endsWith ".scala") - compileArgs(fileNames ++ args, xerrors) + val (files, normArgs) = args match { + case "-deep" :: args1 => (dir.deepFiles, args1) + case _ => (dir.files, args) + } + val fileNames = files.toArray.map(_.toString).filter(_ endsWith ".scala") + compileArgs(fileNames ++ normArgs, xerrors) } def compileFiles(path: String, args: List[String] = Nil)(implicit defaultOptions: List[String]): Unit = { From 1920197ace78b88d56c92e2aa61b0790ec15a786 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 17 Sep 2014 09:55:21 +0200 Subject: [PATCH 03/31] Replace call to unimplemented method runtimeCall Use ref and appliedTo instead. --- src/dotty/tools/dotc/ast/tpd.scala | 4 +--- src/dotty/tools/dotc/transform/TypeTestsCasts.scala | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 3fa08b9c6a6f..216b1c0c5fca 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -358,7 +358,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort)) else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe) } - private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] { def apply(sym: Symbol, tree: Tree) = if (sym.exists) sym @@ -565,7 +564,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree = ((tree: Tree) /: argss)(Apply(_, _)) - def appliedToNone(implicit ctx: Context): Tree = appliedToArgs(Nil) + def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil) def appliedIfMethod(implicit ctx: Context): Tree = { tree.tpe.widen match { @@ -574,7 +573,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } - def appliedToType(targ: Type)(implicit ctx: Context): Tree = appliedToTypes(targ :: Nil) diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 7aaf7d86d73e..6f5f29b6034c 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -69,7 +69,7 @@ trait TypeTestsCasts { } case defn.MultiArrayType(elem, ndims) if isUnboundedGeneric(elem) => def isArrayTest(arg: Tree) = - runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil) + ref(defn.runtimeMethod(nme.isArray)).appliedTo(arg, Literal(Constant(ndims))) if (ndims == 1) isArrayTest(qual) else evalOnce(qual) { qual1 => derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1) From 2b784e151f457be762f925bf43721f8f2cb643c5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 17 Sep 2014 10:35:09 +0200 Subject: [PATCH 04/31] Facturing out EnclosingMethodTraverser from CapturedVars The idea to traverse with currently enclosing methid is also used in LambdaLift and could be used elsewhere. --- src/dotty/tools/dotc/ast/tpd.scala | 16 ++++++++++++ .../tools/dotc/transform/CapturedVars.scala | 25 ++++++++----------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 216b1c0c5fca..45e0aff541eb 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -653,6 +653,22 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args) } + /** A traverser that passes the enlcosing class or method as an argumenr + * to the traverse method. + */ + abstract class EnclosingMethodTraverser(implicit ctx: Context) extends TreeAccumulator[Symbol] { + def traverse(enclMeth: Symbol, tree: Tree): Unit + def apply(enclMeth: Symbol, tree: Tree) = { + tree match { + case _: DefTree if tree.symbol.exists => + traverse(tree.symbol.enclosingMethod, tree) + case _ => + traverse(enclMeth, tree) + } + enclMeth + } + } + // ensure that constructors are fully applied? // ensure that normal methods are fully applied? diff --git a/src/dotty/tools/dotc/transform/CapturedVars.scala b/src/dotty/tools/dotc/transform/CapturedVars.scala index d5bd56bc365e..f13cd56907bb 100644 --- a/src/dotty/tools/dotc/transform/CapturedVars.scala +++ b/src/dotty/tools/dotc/transform/CapturedVars.scala @@ -27,21 +27,16 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor private var captured: mutable.HashSet[Symbol] = _ - private class CollectCaptured(implicit ctx: Context) extends TreeAccumulator[Symbol] { - def apply(enclMeth: Symbol, tree: Tree) = { - tree match { - case id: Ident => - val sym = id.symbol - if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) { - ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth") - captured += sym - } - case tree: DefTree if tree.symbol.exists => - foldOver(tree.symbol.enclosingMethod, tree) - case _ => - foldOver(enclMeth, tree) - } - enclMeth + private class CollectCaptured(implicit ctx: Context) extends EnclosingMethodTraverser { + def traverse(enclMeth: Symbol, tree: Tree) = tree match { + case id: Ident => + val sym = id.symbol + if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) { + ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth") + captured += sym + } + case _ => + foldOver(enclMeth, tree) } def runOver(tree: Tree) = { captured = mutable.HashSet() From 842ba24fd517715c531cf5b290d4229f26b1a330 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 17 Sep 2014 10:01:48 +0200 Subject: [PATCH 05/31] Make enteredAfter, installAfter more forgiving as to when they are called. Instead of requiring to be called a given phase, change the context if that is not the case. --- src/dotty/tools/dotc/core/Denotations.scala | 36 +++++++++---------- .../tools/dotc/core/SymDenotations.scala | 18 +++++----- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 9970c5948274..09b67d08aaa4 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -603,26 +603,26 @@ object Denotations { */ protected def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = { val targetId = phase.next.id - assert(ctx.phaseId == targetId, - s"denotation update for $this called in phase ${ctx.phase}, expected was ${phase.next}") - val current = symbol.current - // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") - // printPeriods(current) - this.nextInRun = current.nextInRun - this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) - if (current.validFor.firstPhaseId == targetId) { - // replace current with this denotation - var prev = current - while (prev.nextInRun ne current) prev = prev.nextInRun - prev.nextInRun = this - current.validFor = Nowhere - } + if (ctx.phaseId != targetId) installAfter(phase)(ctx.withPhase(phase.next)) else { - // insert this denotation after current - current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) - current.nextInRun = this - } + val current = symbol.current + // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") + // printPeriods(current) + this.nextInRun = current.nextInRun + this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) + if (current.validFor.firstPhaseId == targetId) { + // replace current with this denotation + var prev = current + while (prev.nextInRun ne current) prev = prev.nextInRun + prev.nextInRun = this + current.validFor = Nowhere + } else { + // insert this denotation after current + current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) + current.nextInRun = this + } // printPeriods(this) + } } def staleSymbolError(implicit ctx: Context) = { diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 073c9112d3d6..1316e787273e 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1425,19 +1425,17 @@ object SymDenotations { /** If this class has the same `decls` scope reference in `phase` and * `phase.next`, install a new denotation with a cloned scope in `phase.next`. - * @pre Can only be called in `phase.next`. */ - def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = { - assert(ctx.phaseId == phase.next.id) - val prevCtx = ctx.withPhase(phase) - val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo - if (classInfo(prevCtx).decls eq decls) { - copySymDenotation( - info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo), - initFlags = this.flags &~ Frozen).installAfter(phase) + def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = + if (ctx.phaseId != phase.next.id) ensureFreshScopeAfter(phase)(ctx.withPhase(phase.next)) + else { + val prevCtx = ctx.withPhase(phase) + val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo + if (classInfo(prevCtx).decls eq decls) + copySymDenotation(info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo)) + .installAfter(phase) } } - } private case class Uncachable(tp: Type) extends UncachedGroundType From ec5b2ceb199cce38d04c7ad54bd4a3cfc009e858 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 17 Sep 2014 10:03:14 +0200 Subject: [PATCH 06/31] Add isProperlyContainedIn method --- src/dotty/tools/dotc/core/SymDenotations.scala | 5 ++++- src/dotty/tools/dotc/transform/ExplicitOuter.scala | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 1316e787273e..ed123a384bc8 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -372,6 +372,9 @@ object SymDenotations { recur(symbol) } + final def isProperlyContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean = + symbol != boundary && isContainedIn(boundary) + /** Is this denotation static (i.e. with no outer instance)? */ final def isStatic(implicit ctx: Context) = (this is Static) || this.exists && owner.isStaticOwner @@ -916,7 +919,7 @@ object SymDenotations { privateWithin: Symbol = null, annotations: List[Annotation] = null)(implicit ctx: Context) = { // simulate default parameters, while also passing implicit context ctx to the default values - val initFlags1 = if (initFlags != UndefinedFlags) initFlags else this.flags &~ Frozen + val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags) &~ Frozen val info1 = if (info != null) info else this.info val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin val annotations1 = if (annotations != null) annotations else this.annotations diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index aeda6a1d30e3..e07be8603d1b 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -190,7 +190,7 @@ object ExplicitOuter { */ def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = { def isOuter(sym: Symbol) = - sym != cls && !sym.isStaticOwner && cls.isContainedIn(sym) + !sym.isStaticOwner && cls.isProperlyContainedIn(sym) tree match { case thisTree @ This(_) => isOuter(thisTree.symbol) From 0762198f5dbdd71334d4326f8dc03b8088f95e33 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Sep 2014 17:21:39 +0200 Subject: [PATCH 07/31] Fix lifting of application: Lifting an application `f(arg1).f(args)` requires lifting of the whole prefix `f(arg1)`, because `f` might have a side effect. --- src/dotty/tools/dotc/typer/EtaExpansion.scala | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 7c1130b83378..394accd03ae3 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -84,7 +84,7 @@ object EtaExpansion { case TypeApply(fn, targs) => cpy.TypeApply(tree)(liftApp(defs, fn), targs) case Select(pre, name) if isPureRef(tree) => - cpy.Select(tree)(liftApp(defs, pre), name) + cpy.Select(tree)(liftPrefix(defs, pre), name) case Block(stats, expr) => liftApp(defs ++= stats, expr) case New(tpt) => @@ -93,6 +93,18 @@ object EtaExpansion { lift(defs, tree) } + /** Lift prefix `pre` of an application `pre.f(...)` to + * + * val x0 = pre + * x0.f(...) + * + * unless `pre` is a `New` or `pre` is idempotent. + */ + def liftPrefix(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { + case New(_) => tree + case _ => if (isIdempotentExpr(tree)) tree else lift(defs, tree) + } + /** Eta-expanding a tree means converting a method reference to a function value. * @param tree The tree to expand * @param mt The type of the method reference From b4d5ef21a6814227e23fddd7328305cba79aab9b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Sep 2014 17:30:04 +0200 Subject: [PATCH 08/31] Replace tree.withName with {tpd|untpd}.rename.tree Advantage: Can rename typed as well as untyped trees. --- src/dotty/tools/dotc/ast/Trees.scala | 21 ++++++++++++------- src/dotty/tools/dotc/ast/untpd.scala | 10 ++++++--- .../tools/dotc/transform/CapturedVars.scala | 8 +++---- src/dotty/tools/dotc/typer/Applications.scala | 2 +- src/dotty/tools/dotc/typer/Typer.scala | 5 +++-- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index b63f0ad8cc03..19f52b08fdaa 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -408,7 +408,6 @@ object Trees { abstract class NameTree[-T >: Untyped] extends DenotingTree[T] { type ThisTree[-T >: Untyped] <: NameTree[T] def name: Name - def withName(name1: Name)(implicit ctx: Context): untpd.NameTree } /** Tree refers by name to a denotation */ @@ -449,7 +448,6 @@ object Trees { case class Ident[-T >: Untyped] private[ast] (name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Ident[T] - def withName(name: Name)(implicit ctx: Context): untpd.Ident = untpd.cpy.Ident(this)(name) def qualifier: Tree[T] = genericEmptyTree } @@ -460,7 +458,6 @@ object Trees { case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Select[T] - def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(qualifier, name) } class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature) @@ -656,7 +653,6 @@ object Trees { case class SelectFromTypeTree[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = SelectFromTypeTree[T] - def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(qualifier, name) } /** left & right */ @@ -702,7 +698,6 @@ object Trees { extends NameTree[T] with DefTree[T] with PatternTree[T] { type ThisTree[-T >: Untyped] = Bind[T] override def envelope: Position = pos union initialPos - def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name, body) } /** tree_1 | ... | tree_n */ @@ -734,7 +729,6 @@ object Trees { case class ValDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tpt: Tree[T], rhs: Tree[T]) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = ValDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this)(name = name.toTermName) assert(isEmpty || tpt != genericEmptyTree) } @@ -742,7 +736,6 @@ object Trees { case class DefDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T]) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = DefDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.DefDef = untpd.cpy.DefDef(this)(name = name.toTermName) assert(tpt != genericEmptyTree) } @@ -754,7 +747,6 @@ object Trees { case class TypeDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TypeName, rhs: Tree[T]) extends MemberDef[T] { type ThisTree[-T >: Untyped] = TypeDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this)(name = name.toTypeName) /** Is this a definition of a class? */ def isClassDef = rhs.isInstanceOf[Template[_]] @@ -1361,6 +1353,19 @@ object Trees { else foldOver(x1, tree) } } + + def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[T] = { + tree match { + case tree: Ident => cpy.Ident(tree)(newName) + case tree: Select => cpy.Select(tree)(tree.qualifier, newName) + case tree: Bind => cpy.Bind(tree)(newName, tree.body) + case tree: ValDef => cpy.ValDef(tree)(name = newName.asTermName) + case tree: DefDef => cpy.DefDef(tree)(name = newName.asTermName) + case tree: TypeDef => cpy.TypeDef(tree)(name = newName.asTypeName) + case tree: SelectFromTypeTree => cpy.SelectFromTypeTree(tree)(tree.qualifier, newName) + case tree: untpd.PolyTypeDef => untpd.cpy.PolyTypeDef(tree)(tree.mods, newName.asTypeName, tree.tparams, tree.rhs) + } + }.asInstanceOf[tree.ThisTree[T]] } } // ----- Helper functions and classes --------------------------------------- diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 9ac01df9cc93..731917c42bea 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -58,9 +58,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) extends DefTree class PolyTypeDef(mods: Modifiers, name: TypeName, override val tparams: List[TypeDef], rhs: Tree) - extends TypeDef(mods, name, rhs) { - override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this)(mods, name.toTypeName, tparams, rhs) - } + extends TypeDef(mods, name, rhs) // ----- TypeTrees that refer to other tree's symbols ------------------- @@ -414,4 +412,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { super.foldOver(x, tree) } } + + override def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[Untyped] = tree match { + case t: PolyTypeDef => + cpy.PolyTypeDef(t)(t.mods, newName.asTypeName, t.tparams, t.rhs).asInstanceOf[tree.ThisTree[Untyped]] + case _ => super.rename(tree, newName) + } } diff --git a/src/dotty/tools/dotc/transform/CapturedVars.scala b/src/dotty/tools/dotc/transform/CapturedVars.scala index f13cd56907bb..59a7668193ad 100644 --- a/src/dotty/tools/dotc/transform/CapturedVars.scala +++ b/src/dotty/tools/dotc/transform/CapturedVars.scala @@ -17,13 +17,13 @@ import SymUtils._ import collection.{ mutable, immutable } import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet } -class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransformer => +class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ /** the following two members override abstract members in Transform */ val phaseName: String = "capturedVars" - override def treeTransformPhase = thisTransformer.next + override def treeTransformPhase = thisTransform.next private var captured: mutable.HashSet[Symbol] = _ @@ -64,7 +64,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor } def capturedType(vble: Symbol)(implicit ctx: Context): Type = { - val oldInfo = vble.denot(ctx.withPhase(thisTransformer)).info + val oldInfo = vble.denot(ctx.withPhase(thisTransform)).info refCls(oldInfo.classSymbol, vble.isVolatile).typeRef } @@ -86,7 +86,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor override def transformIdent(id: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = { val vble = id.symbol if (captured(vble)) - (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransformer)).info) + (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransform)).info) else id } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 2ff116f461b5..77298e61039a 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -594,7 +594,7 @@ trait Applications extends Compatibility { self: Typer => def followTypeAlias(tree: untpd.Tree): untpd.Tree = { tree match { case tree: untpd.RefTree => - val ttree = typedType(tree.withName(tree.name.toTypeName)) + val ttree = typedType(untpd.rename(tree, tree.name.toTypeName)) ttree.tpe match { case alias: TypeRef if alias.info.isAlias => companionRef(alias) match { diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 9066012f035a..9c1110dd1c40 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -390,7 +390,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case lhsCore: RefTree if setter.exists => val setterTypeRaw = pre select (setterName, setter) val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos) - val lhs2 = lhsCore.withName(setterName).withType(setterType) + val lhs2 = untpd.rename(lhsCore, setterName).withType(setterType) typed(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil)) case _ => reassignmentToVal @@ -1009,7 +1009,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } - protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree withName tree.name.encode + protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree = + untpd.rename(tree, tree.name.encode) def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ { assertPositioned(tree) From 4a8c53038f2c0696367a2ab7955dde92b32aed82 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Sep 2014 17:36:12 +0200 Subject: [PATCH 09/31] Take receiver into account when selecting default getters. Previously, we determined the default getters solely from the method TermRef type. This is wrong if the method is prefix is not a path -- the prefix of the term ref will be a TypeRef and the getter selection will be a SelectFromType tree, which makes no sense. --- src/dotty/tools/dotc/typer/Applications.scala | 96 ++++++++++--------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 77298e61039a..035f19028d7c 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -132,8 +132,7 @@ trait Applications extends Compatibility { self: Typer => protected def appPos: Position - /** If constructing trees, the current function part, which might be - * affected by lifting. EmptyTree otherwise. + /** The current function part, which might be affected by lifting. */ protected def normalizedFun: Tree @@ -251,40 +250,52 @@ trait Applications extends Compatibility { self: Typer => /** Find reference to default parameter getter for parameter #n in current * parameter list, or NoType if none was found */ - def findDefaultGetter(n: Int)(implicit ctx: Context): Type = { - val meth = methRef.symbol - val prefix = - if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else methRef.name - def getterName = prefix.defaultGetterName(n) - def ref(pre: Type, sym: Symbol): Type = - if (pre.exists && sym.isTerm) pre select sym else NoType - if (meth.hasDefaultParams) - methRef.prefix match { - case NoPrefix => - def findDefault(cx: Context): Type = { - if (cx eq NoContext) NoType - else if (cx.scope != cx.outer.scope && - cx.denotNamed(methRef.name).hasAltWith(_.symbol == meth)) { - val denot = cx.denotNamed(getterName) - assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)") - cx.owner.thisType.select(getterName, denot) - } else findDefault(cx.outer) - } - findDefault(ctx) - case mpre => - val cls = meth.owner - val pre = - if (meth.isClassConstructor) { - // default getters for class constructors are found in the companion object - mpre.baseTypeRef(cls) match { - case tp: TypeRef => ref(tp.prefix, cls.companionModule) - case _ => NoType - } - } else mpre - val getter = pre.member(getterName) - ref(pre, getter.symbol) + def findDefaultGetter(n: Int)(implicit ctx: Context): Tree = { + val meth = methRef.symbol.asTerm + val receiver: Tree = methPart(normalizedFun) match { + case Select(receiver, _) => receiver + case mr => mr.tpe.normalizedPrefix match { + case mr: TermRef => ref(mr) + case _ => EmptyTree + } + } + val getterPrefix = + if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else meth.name + def getterName = getterPrefix.defaultGetterName(n) + if (!meth.hasDefaultParams) + EmptyTree + else if (receiver.isEmpty) { + def findGetter(cx: Context): Tree = { + if (cx eq NoContext) EmptyTree + else if (cx.scope != cx.outer.scope && + cx.denotNamed(meth.name).hasAltWith(_.symbol == meth)) { + val denot = cx.denotNamed(getterName) + assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)") + ref(TermRef(cx.owner.thisType, getterName, denot)) + } else findGetter(cx.outer) + } + findGetter(ctx) + } + else { + def selectGetter(qual: Tree): Tree = { + val getterDenot = qual.tpe.member(getterName) + if (getterDenot.exists) qual.select(TermRef(qual.tpe, getterName, getterDenot)) + else EmptyTree + } + if (!meth.isClassConstructor) + selectGetter(receiver) + else { + // default getters for class constructors are found in the companion object + val cls = meth.owner + val companion = cls.companionModule + receiver.tpe.baseTypeRef(cls) match { + case tp: TypeRef if companion.isTerm => + selectGetter(ref(TermRef(tp.prefix, companion.asTerm))) + case _ => + EmptyTree + } } - else NoType + } } /** Match re-ordered arguments against formal parameters @@ -305,13 +316,12 @@ trait Applications extends Compatibility { self: Typer => } def tryDefault(n: Int, args1: List[Arg]): Unit = { - findDefaultGetter(n + numArgs(normalizedFun)) match { - case dref: NamedType => - liftFun() - addTyped(treeToArg(spliceMeth(ref(dref) withPos appPos, normalizedFun)), formal) - matchArgs(args1, formals1, n + 1) - case _ => - missingArg(n) + liftFun() + val getter = findDefaultGetter(n + numArgs(normalizedFun)) + if (getter.isEmpty) missingArg(n) + else { + addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal) + matchArgs(args1, formals1, n + 1) } } @@ -364,7 +374,7 @@ trait Applications extends Compatibility { self: Typer => def fail(msg: => String) = ok = false def appPos = NoPosition - def normalizedFun = EmptyTree + lazy val normalizedFun = ref(methRef) init() } From ee976b89d0d3d01752fcf0fd1aaca9de3e110d6f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Sep 2014 17:36:54 +0200 Subject: [PATCH 10/31] Treat @static as a source flag. It changes meaning drastically so should always be visible. --- src/dotty/tools/dotc/core/Flags.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index e34483f8f75f..64c87ecb865e 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -421,7 +421,7 @@ object Flags { /** Flags representing source modifiers */ final val SourceModifierFlags = commonFlags(Private, Protected, Abstract, Final, - Sealed, Case, Implicit, Override, AbsOverride, Lazy) + Sealed, Case, Implicit, Override, AbsOverride, Lazy, Static) /** Flags representing modifiers that can appear in trees */ final val ModifierFlags = From b157b2887e2354948181108ab2fea28a88d54a9d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Sep 2014 17:38:43 +0200 Subject: [PATCH 11/31] Allow relaxed typing of applications Some transformations encounter applications where new arguments ahve to be supplied. The method type already accounts for these argument but the Application node passed into TreeTransform#transformApply is constructed with a cpy.Apply operation which does a type assignment. That type assignment fails with a parameter mismatch unless relaxedTyping is on. --- src/dotty/tools/dotc/core/Phases.scala | 5 +++++ src/dotty/tools/dotc/typer/TypeAssigner.scala | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 5eb8cd920b18..53c193994457 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -200,6 +200,11 @@ object Phases { */ def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = () + /** If set, allow missing or superfluous arguments in applications + * and type applications. + */ + def relaxedTyping: Boolean = false + def exists: Boolean = true private var myPeriod: Period = Periods.InvalidPeriod diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6b0a8bf9e34a..2f503aa12e92 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -247,7 +247,7 @@ trait TypeAssigner { def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { case fntpe @ MethodType(_, ptypes) => - if (sameLength(ptypes, args)) fntpe.instantiate(args.tpes) + if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) else errorType(i"wrong number of parameters for ${fn.tpe}; expected: ${ptypes.length}", tree.pos) case t => errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos) @@ -259,7 +259,7 @@ trait TypeAssigner { val ownType = fn.tpe.widen match { case pt: PolyType => val argTypes = args.tpes - if (sameLength(argTypes, pt.paramNames)) pt.instantiate(argTypes) + if (sameLength(argTypes, pt.paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos) case _ => errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) From fe0cea49b14e701849f0719ff0395692fbcbe586 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Sep 2014 19:52:07 +0200 Subject: [PATCH 12/31] Take environment into account when typing a closure. Environment parameters do not count in th eresult type. --- src/dotty/tools/dotc/core/Types.scala | 8 ++++++-- src/dotty/tools/dotc/typer/TypeAssigner.scala | 4 +++- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 529452c733ac..6145bdffb809 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -906,10 +906,14 @@ object Types { /** Turn type into a function type. * @pre this is a non-dependent method type. + * @param drop The number of trailing parameters that should be dropped + * when forming the function type. */ - def toFunctionType(implicit ctx: Context): Type = this match { + def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match { case mt @ MethodType(_, formals) if !mt.isDependent => - defn.FunctionType(formals mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType) + val formals1 = if (dropLast == 0) formals else formals dropRight dropLast + defn.FunctionType( + formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType) } /** The signature of this type. This is by default NotAMethod, diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 2f503aa12e92..fea6be9d5056 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -286,7 +286,9 @@ trait TypeAssigner { tree.withType(thenp.tpe | elsep.tpe) def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = - tree.withType(if (target.isEmpty) meth.tpe.widen.toFunctionType else target.tpe) + tree.withType( + if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length) + else target.tpe) def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = tree.withType(body.tpe) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 9c1110dd1c40..ebd33a6ffce6 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1273,7 +1273,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) => pt match { case SAMType(meth) - if wtp <:< meth.info.toFunctionType => + if wtp <:< meth.info.toFunctionType() => // was ... && isFullyDefined(pt, ForceDegree.noBottom) // but this prevents case blocks from implementing polymorphic partial functions, // since we do not know the result parameter a priori. Have to wait until the From ce08c2f7313accc6fb393fbfed0844a3f66e5fa3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Sep 2014 19:54:32 +0200 Subject: [PATCH 13/31] Erasure should keep prefix in termrefs. The current formulation of lambda lift is easier to do if that's the case. --- src/dotty/tools/dotc/TypeErasure.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 1212c9e4dac8..1e3bc5ca6841 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -8,8 +8,8 @@ import util.DotClass /** Erased types are: * - * TypeRef(prefix is ignored, denot is ClassDenotation) - * TermRef(prefix is ignored, denot is SymDenotation) + * TypeRefWithFixedSym(denot is ClassDenotation) + * TermRefWithFixedSym(denot is SymDenotation) * JavaArrayType * AnnotatedType * MethodType @@ -237,7 +237,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild else this(parent) case tp: TermRef => assert(tp.symbol.exists, tp) - TermRef(NoPrefix, tp.symbol.asTerm) + TermRef(this(tp.prefix), tp.symbol.asTerm) case ThisType(_) | SuperType(_, _) => tp case ExprType(rt) => From 6d03e30b8dee04f9b61617a256d730d31288cefb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 14:13:09 +0200 Subject: [PATCH 14/31] Drop type declarations from erased ClassInfo Need to drop all non-class type declarations. --- src/dotty/tools/dotc/TypeErasure.scala | 3 ++- src/dotty/tools/dotc/core/Scopes.scala | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 1e3bc5ca6841..f6c62cf30b8b 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -269,7 +269,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild val parents: List[TypeRef] = if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) - tp.derivedClassInfo(NoPrefix, parents, decls, this(tp.selfType)) + val erasedDecls = decls.filteredScope(d => !d.isType || d.isClass) + tp.derivedClassInfo(NoPrefix, parents, erasedDecls, this(tp.selfType)) // can't replace selftype by NoType because this would lose the sourceModule link } case NoType | NoPrefix | ErrorType | JavaArrayType(_) => diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 54317a4962c8..3f7a4cb2ca20 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -106,7 +106,10 @@ object Scopes { def next(): Symbol = { val r = e.sym; e = lookupNextEntry(e); r } } - /** The denotation set of all the symbols with given name in this scope */ + /** The denotation set of all the symbols with given name in this scope + * Symbols occur in the result in reverse order relative to their occurrence + * in `this.toList`. + */ final def denotsNamed(name: Name, select: SymDenotation => Boolean = selectAll)(implicit ctx: Context): PreDenotation = { var syms: PreDenotation = NoDenotation var e = lookupEntry(name) @@ -118,6 +121,20 @@ object Scopes { syms } + /** The scope that keeps only those symbols from this scope that match the + * given predicates. If all symbols match, returns the scope itself, otherwise + * a copy with the matching symbols. + */ + final def filteredScope(p: Symbol => Boolean)(implicit ctx: Context): Scope = { + var result: MutableScope = null + for (sym <- iterator) + if (!p(sym)) { + if (result == null) result = cloneScope + result.unlink(sym) + } + if (result == null) this else result + } + def implicitDecls(implicit ctx: Context): List[TermRef] = Nil final def toText(printer: Printer): Text = printer.toText(this) From e1be088bb0c59fdb46e2af17a10e5f9892ac8eba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 14:39:33 +0200 Subject: [PATCH 15/31] Uncurry also in DefDef trees, not just in infos. --- src/dotty/tools/dotc/transform/Erasure.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 2505a50b7a77..379a59c0bdd2 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -364,7 +364,7 @@ object Erasure extends TypeTestsCasts{ override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, - vparamss = if (ddef.vparamss.isEmpty) Nil :: Nil else ddef.vparamss, + vparamss = ddef.vparamss.flatten :: Nil, tpt = // keep UnitTypes intact in result position if (ddef.tpt.typeOpt isRef defn.UnitClass) untpd.TypeTree(defn.UnitType) withPos ddef.tpt.pos else ddef.tpt) From 0a04924f8b900bdddd54fe4e43d72a23c36f3782 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 14:40:41 +0200 Subject: [PATCH 16/31] Fix handling of type params in secondary constructors Params are already added by Desugar. No special treatment needed here. Besides primaryConstructor.typeParams is always empty, because term symbols do not have type parameters. The fix turns t2660.scala into an error. I believe the error is correct, hence the test was moved with a comment to neg. --- src/dotty/tools/dotc/typer/Namer.scala | 5 +-- tests/neg/t2660.scala | 47 ++++++++++++++++++++++++++ tests/pos/t2660.scala | 25 -------------- 3 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 tests/neg/t2660.scala delete mode 100644 tests/pos/t2660.scala diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 5ceab475ebb5..3a1f0a98b461 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -632,10 +632,7 @@ class Namer { typer: Typer => completeParams(tparams) vparamss foreach completeParams val isConstructor = name == nme.CONSTRUCTOR - val isSecondaryConstructor = isConstructor && sym != sym.owner.primaryConstructor - def typeParams = - if (isSecondaryConstructor) sym.owner.primaryConstructor.typeParams - else tparams map symbolOfTree + def typeParams = tparams map symbolOfTree def wrapMethType(restpe: Type): Type = { var paramSymss = vparamss.nestedMap(symbolOfTree) // Make sure constructor has one non-implicit parameter list diff --git a/tests/neg/t2660.scala b/tests/neg/t2660.scala new file mode 100644 index 000000000000..85e318915db5 --- /dev/null +++ b/tests/neg/t2660.scala @@ -0,0 +1,47 @@ +// Dotty deviation. The calls here now are classified as ambiguous. + +package hoho + +class G + +class H extends G + +class A[T](x: T) { + + def this(y: G, z: T) = { + this(z) + print(1) + } + + def this(z: H, h: T) = { + this(h) + print(2) + } +} + +object T { + def main(args: Array[String]): Unit = { + implicit def g2h(g: G): H = new H + new A[Int](new H, 23) + // in the context here, either secondary constructor is applicable + // to the other, due to the implicit in scope. So the call is ambiguous. + } +} + + +// A version of t2660 which does not use constructors + +object X { + def f[T](x: T) = ??? + def f[T](y: G, z: T) = ??? + def f[T](z: H, h: T) = ??? +} + +object T2 { + def main(args: Array[String]): Unit = { + implicit def g2h(g: G): H = new H + X.f(new H, 23) + } +} + + diff --git a/tests/pos/t2660.scala b/tests/pos/t2660.scala deleted file mode 100644 index 94a40f7403e8..000000000000 --- a/tests/pos/t2660.scala +++ /dev/null @@ -1,25 +0,0 @@ -package hoho - -class G - -class H extends G - -class A[T](x: T) { - - def this(y: G, z: T) = { - this(z) - print(1) - } - - def this(z: H, h: T) = { - this(h) - print(2) - } -} - -object T { - def main(args: Array[String]): Unit = { - implicit def g2h(g: G): H = new H - new A[Int](new H, 23) - } -} From 41f92ae84b4f07b3c3304748b4073cdd2ef27898 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 14:45:36 +0200 Subject: [PATCH 17/31] Fix tree typing to account for trees after constructors is run. --- .../tools/dotc/transform/TreeChecker.scala | 10 +++---- src/dotty/tools/dotc/typer/ReTyper.scala | 3 ++ src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- src/dotty/tools/dotc/typer/Typer.scala | 29 ++++++++++--------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index ef5baca0729f..5412b66b8bc6 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -46,7 +46,6 @@ class TreeChecker { case _ => Nil } - def check(phasesToRun: Seq[Phase], ctx: Context) = { println(s"checking ${ctx.compilationUnit} after phase ${ctx.phase.prev}") val checkingCtx = ctx.fresh @@ -99,7 +98,8 @@ class TreeChecker { private def checkOwner(tree: untpd.Tree)(implicit ctx: Context): Unit = { def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean = symOwner == ctxOwner || - ctxOwner.isWeakOwner && ownerMatches(symOwner, ctxOwner.owner) + ctxOwner.isWeakOwner && !(ctxOwner is Method | Lazy | Mutable) && + ownerMatches(symOwner, ctxOwner.owner) if(!ownerMatches(tree.symbol.owner, ctx.owner)) { assert(ownerMatches(tree.symbol.owner, ctx.owner), i"bad owner; ${tree.symbol} has owner ${tree.symbol.owner}, expected was ${ctx.owner}\n" + @@ -130,10 +130,10 @@ class TreeChecker { super.typedStats(trees, exprOwner) } - override def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = { - if (ctx.mode.isExpr) - if(!(tree.tpe <:< pt)) + def isPrimaryConstructorReturn = + ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass) + if (ctx.mode.isExpr && !isPrimaryConstructorReturn) assert(tree.tpe <:< pt, s"error at ${sourcePos(tree.pos)}\n" + err.typeMismatchStr(tree.tpe, pt)) diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 31a776fc98f8..6895f997874a 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -71,6 +71,9 @@ class ReTyper extends Typer { override def addTypedModifiersAnnotations(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = typedModifiers(mods, sym) + override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = + parents + override def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match { diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index fea6be9d5056..ccf67b55b9ae 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -236,7 +236,7 @@ trait TypeAssigner { } val owntype = if (!mix.isEmpty) findMixinSuper(cls.info) - else if (inConstrCall) cls.info.firstParent + else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent else { val ps = cls.info.parents if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index ebd33a6ffce6..46fd17d15822 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -374,8 +374,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case lhs => val lhsCore = typedUnadapted(lhs) def lhs1 = typed(untpd.TypedSplice(lhsCore)) + def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields + sym.is(Mutable, butNot = Accessor) || + ctx.owner.isPrimaryConstructor && !sym.is(Method) && sym.owner == ctx.owner.owner lhsCore.tpe match { - case ref: TermRef if ref.symbol is (Mutable, butNot = Accessor) => + case ref: TermRef if canAssign(ref.symbol) => assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info))) case _ => def reassignmentToVal = @@ -843,20 +846,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit result } - /** If this is a real class, make sure its first parent is a - * constructor call. Cannot simply use a type. - */ - def ensureConstrCall(parents: List[Tree]): List[Tree] = { - val firstParent :: otherParents = parents - if (firstParent.isType && !(cls is Trait)) - typed(untpd.New(untpd.TypedSplice(firstParent), Nil))(superCtx) :: otherParents - else parents - } - val mods1 = addTypedModifiersAnnotations(mods, cls) val constr1 = typed(constr).asInstanceOf[DefDef] - val parents1 = ensureConstrCall(ensureFirstIsClass( - parents mapconserve typedParent, cdef.pos.toSynthetic)) + val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.pos.toSynthetic) + val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx) val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible val dummy = localDummy(cls, impl) val body1 = typedStats(body, dummy)(inClassContext(self1.symbol)) @@ -875,6 +868,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // 4. Polymorphic type defs override nothing. } + /** If this is a real class, make sure its first parent is a + * constructor call. Cannot simply use a type. Overridden in ReTyper. + */ + def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = { + val firstParent :: otherParents = parents + if (firstParent.isType && !(cls is Trait)) + typed(untpd.New(untpd.TypedSplice(firstParent), Nil)) :: otherParents + else parents + } + /** Overridden in retyper */ def checkVariance(tree: Tree)(implicit ctx: Context) = VarianceChecker.check(tree) From 7d39cbc6e547d5f8a9fef732a28e75e16227ed90 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 14:48:10 +0200 Subject: [PATCH 18/31] New utility methods in SymUtils --- src/dotty/tools/dotc/ast/TreeTypeMap.scala | 9 ++----- src/dotty/tools/dotc/transform/SymUtils.scala | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 56602cca91e1..33be848c1ed1 100644 --- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -6,6 +6,7 @@ import core._ import Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, Annotations._, Trees._, Symbols._ import Denotations._, Decorators._ +import dotty.tools.dotc.transform.SymUtils._ /** A map that applies three functions and a substitution together to a tree and * makes sure they are coordinated so that the result is well-typed. The functions are @@ -42,13 +43,7 @@ final class TreeTypeMap( import tpd._ /** If `sym` is one of `oldOwners`, replace by corresponding symbol in `newOwners` */ - def mapOwner(sym: Symbol) = { - def loop(from: List[Symbol], to: List[Symbol]): Symbol = - if (from.isEmpty) sym - else if (sym eq from.head) to.head - else loop(from.tail, to.tail) - loop(oldOwners, newOwners) - } + def mapOwner(sym: Symbol) = sym.subst(oldOwners, newOwners) /** Replace occurrences of `This(oldOwner)` in some prefix of a type * by the corresponding `This(newOwner)`. diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index df0ac59ae930..5ae9aba63217 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -7,6 +7,8 @@ import Contexts._ import Symbols._ import Decorators._ import Names._ +import StdNames._ +import Flags._ import language.implicitConversions object SymUtils { @@ -17,9 +19,31 @@ object SymUtils { * that are needed in the transformer pipeline. */ class SymUtils(val self: Symbol) extends AnyVal { + import SymUtils._ def isTypeTestOrCast(implicit ctx: Context): Boolean = self == defn.Any_asInstanceOf || self == defn.Any_isInstanceOf def isVolatile(implicit ctx: Context) = self.hasAnnotation(defn.VolatileAnnot) + + /** If this is a constructor, its owner: otherwise this. */ + final def skipConstructor(implicit ctx: Context): Symbol = + if (self.isConstructor) self.owner else self + + final def isAnonymousFunction(implicit ctx: Context): Boolean = + self.is(Method) && (self.denot.initial.asSymDenotation.name startsWith nme.ANON_FUN) + + /** The logically enclosing method or class for this symbol. + * Instead of constructors one always picks the enclosing class. + */ + final def enclosure(implicit ctx: Context) = self.owner.enclosingMethod.skipConstructor + + /** Apply symbol/symbol substitution to this symbol */ + def subst(from: List[Symbol], to: List[Symbol]): Symbol = { + def loop(from: List[Symbol], to: List[Symbol]): Symbol = + if (from.isEmpty) self + else if (self eq from.head) to.head + else loop(from.tail, to.tail) + loop(from, to) + } } From 360d10e789722d0423c7d88a858f7a20d8d5644c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 14:50:45 +0200 Subject: [PATCH 19/31] Account for PolyProto and IgnoredProto types in expectedTypeStr --- src/dotty/tools/dotc/typer/ErrorReporting.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 1f55df2bcb64..e96e04b1a449 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -56,10 +56,12 @@ object ErrorReporting { else "" def expectedTypeStr(tp: Type): String = tp match { + case tp: PolyProto => + d"type arguments [${tp.targs}%, %] and ${expectedTypeStr(tp.resultType)}" case tp: FunProto => val result = tp.resultType match { - case tp: WildcardType => "" - case tp => d"and expected result type $tp" + case _: WildcardType | _: IgnoredProto => "" + case tp => d" and expected result type $tp" } d"arguments (${tp.typedArgs.tpes}%, %)$result" case _ => From f65f0f44234057e9e35cd9ddc806a4d568d7b5f0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 14:52:04 +0200 Subject: [PATCH 20/31] Fix to primaryConstructor Primary constructor was picking last constructor instead of first one. This is now fixed. Also, added paramAccessors utility method. --- src/dotty/tools/dotc/core/Contexts.scala | 2 +- src/dotty/tools/dotc/core/Denotations.scala | 5 ++++- src/dotty/tools/dotc/core/SymDenotations.scala | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 6b9b1dec7a08..3b14872b7a7e 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -282,7 +282,7 @@ object Contexts { * from constructor parameters to class paramater accessors. */ def superCallContext: Context = { - val locals = newScopeWith(owner.decls.filter(_ is ParamAccessor).toList: _*) + val locals = newScopeWith(owner.asClass.paramAccessors: _*) superOrThisCallContext(owner.primaryConstructor, locals) } diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 09b67d08aaa4..82fd60fa0cef 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -681,6 +681,7 @@ object Denotations { // ------ PreDenotation ops ---------------------------------------------- final def first = this + final def last = this final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym) final def containsSig(sig: Signature)(implicit ctx: Context) = @@ -766,8 +767,9 @@ object Denotations { /** A denotation in the group exists */ def exists: Boolean - /** First denotation in the group */ + /** First/last denotation in the group */ def first: Denotation + def last: Denotation /** Convert to full denotation by &-ing all elements */ def toDenot(pre: Type)(implicit ctx: Context): Denotation @@ -832,6 +834,7 @@ object Denotations { assert(denots1.exists && denots2.exists, s"Union of non-existing denotations ($denots1) and ($denots2)") def exists = true def first = denots1.first + def last = denots2.last def toDenot(pre: Type)(implicit ctx: Context) = (denots1 toDenot pre) & (denots2 toDenot pre, pre) def containsSym(sym: Symbol) = diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ed123a384bc8..378d95b81b87 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1423,9 +1423,15 @@ object SymDenotations { override def primaryConstructor(implicit ctx: Context): Symbol = { val cname = if (this is ImplClass) nme.IMPLCLASS_CONSTRUCTOR else nme.CONSTRUCTOR - decls.denotsNamed(cname).first.symbol + decls.denotsNamed(cname).last.symbol // denotsNamed returns Symbols in reverse order of occurrence } + /** The parameter accessors of this class. Term and type accessors, + * getters and setters are all returned int his list + */ + def paramAccessors(implicit ctx: Context): List[Symbol] = + decls.filter(_ is ParamAccessor).toList + /** If this class has the same `decls` scope reference in `phase` and * `phase.next`, install a new denotation with a cloned scope in `phase.next`. */ From 0e32cf8d9a2d3963be9424ed4d1d39b0c4c6ab7c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 15:03:29 +0200 Subject: [PATCH 21/31] Add constructors phase --- src/dotty/tools/dotc/Compiler.scala | 4 +- .../tools/dotc/printing/RefinedPrinter.scala | 7 +- .../tools/dotc/transform/Constructors.scala | 131 ++++++++++++++++-- test/dotc/tests.scala | 8 +- 4 files changed, 124 insertions(+), 26 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 258cab36889a..87cac9799612 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -63,7 +63,9 @@ class Compiler { new InterceptedMethods, new Literalize), List(new Erasure), - List(new CapturedVars) + List(new CapturedVars), + List(new Constructors)/*, + List(new LambdaLift)*/ ) var runId = 1 diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index d66d93a16f80..ae75bcb2d365 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -326,10 +326,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { typeDefText(optText(rhs)(" = " ~ _)) } } - case Template(DefDef(mods, _, tparams, vparamss, _, _), parents, self, stats) => + case Template(constr @ DefDef(mods, _, tparams, vparamss, _, rhs), parents, self, stats) => val tparamsTxt = tparamsText(tparams) + val primaryConstrs = if (rhs.isEmpty) Nil else constr :: Nil val prefix: Text = - if (vparamss.isEmpty) tparamsTxt + if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt else { var modsText = modText(mods, "") if (mods.hasAnnotations && !mods.hasFlags) modsText = modsText ~~ " this" @@ -340,7 +341,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val selfName = if (self.name == nme.WILDCARD) "this" else self.name.toString (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close } provided !self.isEmpty - val bodyText = "{" ~~ selfText ~~ toTextGlobal(stats, "\n") ~ "}" + val bodyText = "{" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: stats, "\n") ~ "}" prefix ~~ (" extends" provided ownerIsClass) ~~ parentsText ~~ bodyText case Import(expr, selectors) => def selectorText(sel: Tree): Text = sel match { diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 34c90fdc2d31..d03b90a27ecd 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -1,27 +1,128 @@ -package dotty.tools.dotc.transform +package dotty.tools.dotc +package transform +import core._ import TreeTransforms._ import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.StdNames._ +import Phases._ +import ast._ +import Trees._ +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import ExplicitOuter.outerParamAccessor /** This transform moves initializers from body to constructor. - * Right now it's a dummy. - * Awaiting for real implemetation */ -class Constructors extends MiniPhaseTransform { +class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform => + import tpd._ override def phaseName: String = "constructors" - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - if(tree.symbol.isClassConstructor) { - val claz = tree.symbol.enclosingClass.asClass - val zuper = claz.info.parents.head.typeSymbol - cpy.DefDef(tree)(rhs = { - val parentCall = - Super(This(claz), tpnme.EMPTY, true).select(zuper.primaryConstructor).appliedToNone - if(tree.rhs.isEmpty) parentCall - else Block(List(parentCall), tree.rhs) - }) - } else tree + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) + + override def treeTransformPhase = thisTransform.next + + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { + def ownerBecomesConstructor(owner: Symbol): Boolean = + (owner.isLocalDummy || + owner.isTerm && !owner.is(Method) && owner.owner.isClass) && + !owner.enclosingClass.is(Trait) // TODO: Remove qualification once Mixin is operational + if (ownerBecomesConstructor(sym.owner)) + sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor) + else sym + } + + private def intoConstr(accessors: List[Symbol], params: List[Symbol]) = new TreeMap { + override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { + case Ident(_) | Select(This(_), _) => + val sym = tree.symbol + if (sym is ParamAccessor) { + val param = sym.subst(accessors, params) + if (param ne sym) ref(param).withPos(tree.pos) + else tree + } + else tree + case Apply(fn, Nil) => + val fn1 = transform(fn) + if ((fn1 ne fn) && + fn1.symbol.is(Param) && + fn1.symbol.owner.isPrimaryConstructor) { + // Two possible cases, which each need their adaptation: + if (fn1.symbol.initial.info.isInstanceOf[ExprType]) + // it's either a call-by-name parameter, which is erased to Function0, + // then we need to insert an apply. + cpy.Apply(tree)(Select(fn1, nme.apply), Nil).ensureConforms(tree.tpe) + else + // or original accessor was an alias accessor, then we need to drop the () + fn1 + } + else cpy.Apply(tree)(fn1, Nil) + case _ => + super.transform(tree) + } + } + + private def splitStats(stats: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) = stats match { + case stat :: stats1 => + val (constrStats, clsStats) = splitStats(stats1) + stat match { + case stat @ ValDef(mods, name, tpt, rhs) if !rhs.isEmpty => + val inits = + if (isWildcardArg(rhs)) Nil + else Assign(ref(stat.symbol), rhs).withPos(stat.pos) :: Nil + (inits ::: constrStats, cpy.ValDef(stat)(rhs = EmptyTree) :: clsStats) + case _: DefTree => + (constrStats, stat :: clsStats) + case _ => + (stat :: constrStats, clsStats) + } + case Nil => + (Nil, Nil) + } + + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { + val cls = ctx.owner.asClass + if (cls is Trait) tree + else { + val constr @ DefDef(_, nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr + val (superApp @ Apply( + superSel @ Select( + superNew @ New(superType), + nme.CONSTRUCTOR), + superArgs)) :: traitParents = tree.parents + var accessors = cls.paramAccessors.filterNot(_.isSetter) + var vparamsWithOuter = vparams + if (!accessors.hasSameLengthAs(vparams)) { + accessors.reverse match { + case last :: _ if (last.name == nme.OUTER) => + accessors = last :: accessors.init + vparamsWithOuter = ValDef(last.asTerm) :: vparams + case _ => + } + assert(accessors.hasSameLengthAs(vparamsWithOuter), + i"lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter") + } + val mappedArgs = superArgs.map( + intoConstr(accessors, vparamsWithOuter.map(_.symbol)).transform) + val superCall = + cpy.Apply(superApp)( + cpy.Select(superSel)( + Super(This(cls), tpnme.EMPTY, inConstrCall = true).withPos(superNew.pos), + nme.CONSTRUCTOR), + mappedArgs) + val (constrStats, clsStats) = splitStats(tree.body) + def normalizeOwner(stat: Tree) = { + } + cpy.Template(tree)( + constr = cpy.DefDef(constr)(rhs = Block(superCall :: constrStats, unitLiteral)), + parents = superType :: traitParents, + body = clsStats) + } } } \ No newline at end of file diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index ceadacaef9fd..2816224f16d9 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -31,7 +31,6 @@ class tests extends CompilerTest { val dotcDir = "./src/dotty/" - @Test def pos_t2168_pat = compileFile(posDir, "t2168", doErase) @Test def pos_erasure = compileFile(posDir, "erasure", doErase) @Test def pos_Coder() = compileFile(posDir, "Coder", doErase) @Test def pos_blockescapes() = compileFile(posDir, "blockescapes", doErase) @@ -49,7 +48,6 @@ class tests extends CompilerTest { @Test def pos_desugar() = compileFile(posDir, "desugar", doErase) @Test def pos_sigs() = compileFile(posDir, "sigs", doErase) @Test def pos_typers() = compileFile(posDir, "typers", doErase) - @Test def pos_typedidents() = compileFile(posDir, "typedIdents", doErase) @Test def pos_assignments() = compileFile(posDir, "assignments", doErase) @Test def pos_packageobject() = compileFile(posDir, "packageobject", doErase) @@ -97,6 +95,7 @@ class tests extends CompilerTest { @Test def nef_t1279a = compileFile(negDir, "t1279a", xerrors = 1) @Test def neg_t1843 = compileFile(negDir, "t1843", xerrors = 1) @Test def neg_t1843_variances = compileFile(negDir, "t1843-variances", xerrors = 1) + @Test def neg_t2660_ambi = compileFile(negDir, "t2660", xerrors = 2) @Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 2) @Test def neg_subtyping = compileFile(negDir, "subtyping", xerrors = 1) @Test def neg_variances = compileFile(negDir, "variances", xerrors = 2) @@ -108,15 +107,11 @@ class tests extends CompilerTest { @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) @Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice)(allowDeepSubtypes) @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice)(allowDeepSubtypes) - @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice) - @Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice) @Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice) - @Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice) @Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", twice) - @Test def dotc_util = compileDir(dotcDir + "tools/dotc/util", twice) @Test def tools_io = compileDir(dotcDir + "tools/io", twice) @Test def tools = compileDir(dotcDir + "tools", "-deep" :: twice)(allowDeepSubtypes) @@ -129,7 +124,6 @@ class tests extends CompilerTest { "-Xprompt", "#runs", "2")) - @Test def testIssue_34 = compileArgs(Array( dotcDir + "tools/dotc/config/Properties.scala", dotcDir + "tools/dotc/config/PathResolver.scala", From 77ad36da02c230cc43a4fdfe336a7d0a613a5c36 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 16:10:04 +0200 Subject: [PATCH 22/31] Combine captured vars and constructors into one macro phase. Needed to fix a problem in CapturedVars to make this work. --- src/dotty/tools/dotc/Compiler.scala | 3 +-- src/dotty/tools/dotc/transform/CapturedVars.scala | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 87cac9799612..63f79c8d391f 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -63,8 +63,7 @@ class Compiler { new InterceptedMethods, new Literalize), List(new Erasure), - List(new CapturedVars), - List(new Constructors)/*, + List(new CapturedVars, new Constructors)/*, List(new LambdaLift)*/ ) diff --git a/src/dotty/tools/dotc/transform/CapturedVars.scala b/src/dotty/tools/dotc/transform/CapturedVars.scala index 59a7668193ad..91b81fea5d0a 100644 --- a/src/dotty/tools/dotc/transform/CapturedVars.scala +++ b/src/dotty/tools/dotc/transform/CapturedVars.scala @@ -45,7 +45,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor } override def init(implicit ctx: Context, info: TransformerInfo): Unit = - (new CollectCaptured).runOver(ctx.compilationUnit.tpdTree) + (new CollectCaptured)(ctx.withPhase(thisTransform)).runOver(ctx.compilationUnit.tpdTree) override def transformSym(sd: SymDenotation)(implicit ctx: Context): SymDenotation = if (captured(sd.symbol)) { From 2081bd92fc6cbc363bace82bc797a6fcff0e900f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 23 Sep 2014 12:43:08 +0200 Subject: [PATCH 23/31] Fix rewriting of by-name class parameter accessors. Like the corresponding parameters, these also need to be rewritten to function types. --- src/dotty/tools/dotc/transform/ElimByName.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ElimByName.scala b/src/dotty/tools/dotc/transform/ElimByName.scala index 5925ffa721ff..e656c8438958 100644 --- a/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/src/dotty/tools/dotc/transform/ElimByName.scala @@ -4,6 +4,7 @@ package transform import TreeTransforms._ import core.DenotTransformers._ import core.Symbols._ +import core.SymDenotations._ import core.Contexts._ import core.Types._ import core.Flags._ @@ -75,9 +76,12 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform cpy.Apply(tree)(tree.fun, args1) } + private def becomesFunction(symd: SymDenotation)(implicit ctx: Context) = + (symd is Param) || (symd is (ParamAccessor, butNot = Method)) + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = { val origDenot = originalDenotation(tree) - if ((origDenot is Param) && (origDenot.info.isInstanceOf[ExprType])) + if (becomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) tree.select(defn.Function0_apply).appliedToNone else tree } @@ -98,6 +102,6 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform } def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - if (sym is Param) transformParamInfo(tp) + if (becomesFunction(sym)) transformParamInfo(tp) else elimByNameParams(tp) } From 40179ca28bbda22884e4d3d5357864bcf05c2da6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 23 Sep 2014 12:44:31 +0200 Subject: [PATCH 24/31] Completed constructors phase 1) Type parameters are now copied to accessors 2) Constructors also work for traits 2) makes it possible do to mixin after constructors. --- src/dotty/tools/dotc/core/Flags.scala | 7 +- .../tools/dotc/transform/Constructors.scala | 205 ++++++++++++------ 2 files changed, 141 insertions(+), 71 deletions(-) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 64c87ecb865e..de27282fbb44 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -543,12 +543,15 @@ object Flags { /** Labeled private[this] */ final val PrivateLocal = allOf(Private, Local) - /** A private parameter accessor */ + /** A private[this] parameter accessor */ final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor) - /** A private parameter */ + /** A private[this] parameter */ final val PrivateLocalParam = allOf(Private, Local, Param) + /** A private parameter accessor */ + final val PrivateParamAccessor = allOf(Private, ParamAccessor) + /** A local parameter */ final val ParamAndLocal = allOf(Param, Local) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index d03b90a27ecd..3d2f5a0d7079 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -16,9 +16,11 @@ import SymDenotations._ import Types._ import Decorators._ import DenotTransformers._ -import ExplicitOuter.outerParamAccessor +import collection.mutable -/** This transform moves initializers from body to constructor. +/** This transform + * - moves initializers from body to constructor. + * - makes all supercalls explicit */ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform => import tpd._ @@ -28,21 +30,37 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor override def treeTransformPhase = thisTransform.next + /** Symbols that are owned by either or a class field move into the + * primary constructor. + */ override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { def ownerBecomesConstructor(owner: Symbol): Boolean = - (owner.isLocalDummy || - owner.isTerm && !owner.is(Method) && owner.owner.isClass) && - !owner.enclosingClass.is(Trait) // TODO: Remove qualification once Mixin is operational + (owner.isLocalDummy || owner.isTerm && !owner.is(Method)) && + owner.owner.isClass if (ownerBecomesConstructor(sym.owner)) sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor) else sym } - private def intoConstr(accessors: List[Symbol], params: List[Symbol]) = new TreeMap { + /** @return true if after ExplicitOuter, all references from this tree go via an + * outer link, so no parameter accessors need to be rewired to parameters + */ + private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) = + tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall) + + /** Adjustments performed when moving code into the constructor: + * (1) Replace references to param accessors by constructor parameters + * except possibly references to mutable variables, if `excluded = Mutable`. + * (Mutable parameters should be replaced only during the super call) + * (2) If the parameter accessor reference was to an alias getter, + * drop the () when replacing by the parameter. + */ + class IntoConstrMap(accessors: List[Symbol], params: List[Symbol]) extends TreeMap { + private var excluded: FlagSet = _ override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case Ident(_) | Select(This(_), _) => val sym = tree.symbol - if (sym is ParamAccessor) { + if (sym is (ParamAccessor, butNot = excluded)) { val param = sym.subst(accessors, params) if (param ne sym) ref(param).withPos(tree.pos) else tree @@ -50,79 +68,128 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor else tree case Apply(fn, Nil) => val fn1 = transform(fn) - if ((fn1 ne fn) && - fn1.symbol.is(Param) && - fn1.symbol.owner.isPrimaryConstructor) { - // Two possible cases, which each need their adaptation: - if (fn1.symbol.initial.info.isInstanceOf[ExprType]) - // it's either a call-by-name parameter, which is erased to Function0, - // then we need to insert an apply. - cpy.Apply(tree)(Select(fn1, nme.apply), Nil).ensureConforms(tree.tpe) - else - // or original accessor was an alias accessor, then we need to drop the () - fn1 - } + if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor) + fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass else cpy.Apply(tree)(fn1, Nil) case _ => - super.transform(tree) + if (noDirectRefsFrom(tree)) tree else super.transform(tree) } - } - private def splitStats(stats: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) = stats match { - case stat :: stats1 => - val (constrStats, clsStats) = splitStats(stats1) - stat match { - case stat @ ValDef(mods, name, tpt, rhs) if !rhs.isEmpty => - val inits = - if (isWildcardArg(rhs)) Nil - else Assign(ref(stat.symbol), rhs).withPos(stat.pos) :: Nil - (inits ::: constrStats, cpy.ValDef(stat)(rhs = EmptyTree) :: clsStats) - case _: DefTree => - (constrStats, stat :: clsStats) - case _ => - (stat :: constrStats, clsStats) - } - case Nil => - (Nil, Nil) + def apply(tree: Tree, excluded: FlagSet)(implicit ctx: Context): Tree = { + this.excluded = excluded + transform(tree) + } } override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { val cls = ctx.owner.asClass - if (cls is Trait) tree - else { - val constr @ DefDef(_, nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr - val (superApp @ Apply( - superSel @ Select( - superNew @ New(superType), - nme.CONSTRUCTOR), - superArgs)) :: traitParents = tree.parents - var accessors = cls.paramAccessors.filterNot(_.isSetter) - var vparamsWithOuter = vparams - if (!accessors.hasSameLengthAs(vparams)) { - accessors.reverse match { - case last :: _ if (last.name == nme.OUTER) => - accessors = last :: accessors.init - vparamsWithOuter = ValDef(last.asTerm) :: vparams + val constr @ DefDef(_, nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr + + // Produce aligned accessors and constructor parameters. We have to adjust + // for any outer parameters, which are last in the sequence of original + // parameter accessors but should come first in the constructor parameter list. + var accessors = cls.paramAccessors.filterNot(_.isSetter) + var vparamsWithOuter = vparams + if (!accessors.hasSameLengthAs(vparams)) { + accessors.reverse match { + case last :: _ if (last.name == nme.OUTER) => + accessors = last :: accessors.init // align wth calling convention + vparamsWithOuter = ValDef(last.asTerm) :: vparams + case _ => + } + assert(accessors.hasSameLengthAs(vparamsWithOuter), + i"lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter") + } + val paramSyms = vparamsWithOuter map (_.symbol) + + val intoConstr = new IntoConstrMap(accessors, paramSyms) + val superCalls = new mutable.ListBuffer[Tree] + + // If parent is a constructor call, pull out the call into a separate + // supercall constructor, which gets appended to `superCalls`, and keep + // only the type. + def normalizeParent(tree: Tree) = tree match { + case superApp @ Apply( + superSel @ Select( + superNew @ New(superType), + nme.CONSTRUCTOR), + superArgs) => + val toClass = !superType.symbol.is(Trait) + val mappedArgs = superArgs.map( + intoConstr(_, excluded = if (toClass) Mutable else EmptyFlags)) + val receiver = + if (toClass) Super(This(cls), tpnme.EMPTY, inConstrCall = true) + else This(cls) + superCalls += cpy.Apply(superApp)( + receiver.withPos(superNew.pos) + .select(superSel.symbol).withPos(superSel.pos), + mappedArgs) + superType + case tree: TypeTree => tree + } + val parentTypeTrees = tree.parents.map(normalizeParent) + + // Split class body into statements that go into constructor and + // definitions that are kept as members of the class. + def splitStats(stats: List[Tree]): (List[Tree], List[Tree]) = stats match { + case stat :: stats1 => + val (constrStats, clsStats) = splitStats(stats1) + stat match { + case stat @ ValDef(mods, name, tpt, rhs) => + val inits = + if (rhs.isEmpty || isWildcardArg(rhs)) Nil + else Assign(ref(stat.symbol), intoConstr(rhs, excluded = Mutable)) + .withPos(stat.pos) :: Nil + (inits ::: constrStats, cpy.ValDef(stat)(rhs = EmptyTree) :: clsStats) + case _: DefTree => + (constrStats, stat :: clsStats) case _ => + (intoConstr(stat, excluded = Mutable) :: constrStats, clsStats) } - assert(accessors.hasSameLengthAs(vparamsWithOuter), - i"lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter") - } - val mappedArgs = superArgs.map( - intoConstr(accessors, vparamsWithOuter.map(_.symbol)).transform) - val superCall = - cpy.Apply(superApp)( - cpy.Select(superSel)( - Super(This(cls), tpnme.EMPTY, inConstrCall = true).withPos(superNew.pos), - nme.CONSTRUCTOR), - mappedArgs) - val (constrStats, clsStats) = splitStats(tree.body) - def normalizeOwner(stat: Tree) = { + case Nil => + (Nil, Nil) + } + val (constrStats, clsStats) = splitStats(tree.body) + + // Collect all private parameter accessors that need to be retained + // because they are accessed after the constructor has finished. + val collectRetained = new TreeAccumulator[Set[Symbol]] { + override def apply(retained: Set[Symbol], tree: Tree) = tree match { + case tree: RefTree => + val sym = tree.symbol + foldOver( + if (sym.is(PrivateParamAccessor) && sym.owner == cls) retained + sym else retained, + tree) + case _ => + if (noDirectRefsFrom(tree)) retained else foldOver(retained, tree) } - cpy.Template(tree)( - constr = cpy.DefDef(constr)(rhs = Block(superCall :: constrStats, unitLiteral)), - parents = superType :: traitParents, - body = clsStats) } + val retainedPrivate = collectRetained(collectRetained(Set[Symbol](), constrStats), clsStats) + def isRetained(acc: Symbol) = + (!acc.is(Private) || acc.is(NotJavaPrivate) || retainedPrivate(acc)) + + val accessorFields = accessors.filterNot(_ is Method) + val (retainedAccessors, droppedAccessors) = accessorFields.partition(isRetained) + + // The initializers for the retained accessors */ + val copyParams = retainedAccessors.map(acc => + Assign(ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) + + // Drop accessors that are not retained from class scope + if (droppedAccessors.nonEmpty) { + val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error + cls.copy( + info = clsInfo.derivedClassInfo( + decls = clsInfo.decls.filteredScope(!droppedAccessors.contains(_)))) + } + + cpy.Template(tree)( + constr = cpy.DefDef(constr)( + rhs = Block(superCalls.toList ::: copyParams ::: constrStats, unitLiteral)), + parents = parentTypeTrees, + body = clsStats filter { + case vdef: ValDef => !droppedAccessors.contains(vdef.symbol) + case _ => true + }) } } \ No newline at end of file From 88bfdebe05cab1dec85859ba758ee7f4399f086e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 23 Sep 2014 14:05:14 +0200 Subject: [PATCH 25/31] Made TypeVars uncahable keys TypeVars can appear as keys in baseType caches. The problem is that their base types depend on their instantiation, which is not always know yet when the test is performed. So results of baseType on type variables should never be cached. Todo: check whether there are any other caching problems involving typevars. --- src/dotty/tools/dotc/core/SymDenotations.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 378d95b81b87..55611211487c 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1445,7 +1445,6 @@ object SymDenotations { .installAfter(phase) } } - private case class Uncachable(tp: Type) extends UncachedGroundType /** The denotation of a package class. From 056c7f05ebc04097030a57ab1b2406c87ab43e54 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 23 Sep 2014 15:42:55 +0200 Subject: [PATCH 26/31] Fix caching logic in baseTypeRef Typevars can be parts of larger types or underlying types of other types, which renders these other types uncacheable. The new logic takes account of that. --- .../tools/dotc/core/SymDenotations.scala | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 55611211487c..711a76ad50b3 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1317,6 +1317,18 @@ object SymDenotations { case _ => bt } + def inCache(tp: Type) = baseTypeRefCache.containsKey(tp) + + /** Can't cache types containing type variables which are uninstantiated + * or whose instances can change, depending on typerstate. + */ + def isCachable(tp: Type): Boolean = tp match { + case tp: TypeProxy => inCache(tp.underlying) + case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) + case tp: TypeVar => tp.inst.exists && inCache(tp.inst) + case _ => true + } + def computeBaseTypeRefOf(tp: Type): Type = { Stats.record("computeBaseTypeOf") if (symbol.isStatic && tp.derivesFrom(symbol)) @@ -1333,9 +1345,6 @@ object SymDenotations { case _ => baseTypeRefOf(tp.underlying) } - case tp: TypeVar => - if (tp.inst.exists) computeBaseTypeRefOf(tp.inst) - else Uncachable(computeBaseTypeRefOf(tp.underlying)) case tp: TypeProxy => baseTypeRefOf(tp.underlying) case AndType(tp1, tp2) => @@ -1356,15 +1365,9 @@ object SymDenotations { var basetp = baseTypeRefCache get tp if (basetp == null) { baseTypeRefCache.put(tp, NoPrefix) - val computedBT = computeBaseTypeRefOf(tp) - basetp = computedBT match { - case Uncachable(basetp) => - baseTypeRefCache.remove(tp) - computedBT - case basetp => - baseTypeRefCache.put(tp, basetp) - basetp - } + basetp = computeBaseTypeRefOf(tp) + if (isCachable(tp)) baseTypeRefCache.put(tp, basetp) + else baseTypeRefCache.remove(tp) } else if (basetp == NoPrefix) { throw CyclicReference(this) } @@ -1444,8 +1447,7 @@ object SymDenotations { copySymDenotation(info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo)) .installAfter(phase) } - } - private case class Uncachable(tp: Type) extends UncachedGroundType + } /** The denotation of a package class. * It overrides ClassDenotation to take account of package objects when looking for members From eae997ccafb080abeeb6b144cdea2544bb7b9a45 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 24 Sep 2014 14:02:56 +0200 Subject: [PATCH 27/31] Added method to help traversing children in a TreeTraverser --- src/dotty/tools/dotc/ast/Trees.scala | 3 ++- src/dotty/tools/dotc/typer/VarianceChecker.scala | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 19f52b08fdaa..fb9ad24b3b0d 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -1336,6 +1336,7 @@ object Trees { abstract class TreeTraverser extends TreeAccumulator[Unit] { def traverse(tree: Tree): Unit def apply(x: Unit, tree: Tree) = traverse(tree) + protected def traverseChildren(tree: Tree) = foldOver((), tree) } /** Fold `f` over all tree nodes, in depth-first, prefix order */ @@ -1361,9 +1362,9 @@ object Trees { case tree: Bind => cpy.Bind(tree)(newName, tree.body) case tree: ValDef => cpy.ValDef(tree)(name = newName.asTermName) case tree: DefDef => cpy.DefDef(tree)(name = newName.asTermName) + case tree: untpd.PolyTypeDef => untpd.cpy.PolyTypeDef(tree)(tree.mods, newName.asTypeName, tree.tparams, tree.rhs) case tree: TypeDef => cpy.TypeDef(tree)(name = newName.asTypeName) case tree: SelectFromTypeTree => cpy.SelectFromTypeTree(tree)(tree.qualifier, newName) - case tree: untpd.PolyTypeDef => untpd.cpy.PolyTypeDef(tree)(tree.mods, newName.asTypeName, tree.tparams, tree.rhs) } }.asInstanceOf[tree.ThisTree[T]] } diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala index 5865f0133f67..e1f50ded9d74 100644 --- a/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -125,7 +125,7 @@ class VarianceChecker()(implicit ctx: Context) { ctx.debuglog(s"Skipping variance check of ${sym.showDcl}") case tree: TypeDef => checkVariance(sym) - foldOver((), tree) + traverseChildren(tree) case tree: ValDef => checkVariance(sym) case DefDef(_, _, tparams, vparamss, _, _) => @@ -133,7 +133,7 @@ class VarianceChecker()(implicit ctx: Context) { tparams foreach traverse vparamss foreach (_ foreach traverse) case Template(_, _, _, body) => - foldOver((), tree) + traverseChildren(tree) case _ => } } From 5708b178f295a023a850fddde1b41a7ae1c409b8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 24 Sep 2014 14:05:43 +0200 Subject: [PATCH 28/31] Move private fields into constructor Private fields that are accessed only from the constructor, and are accessed only after they are properly initialized are now moved into the constructor. This avoids creating a redundant objetc field. Good example: gcd in Rationals (see constrs.scala). --- .../tools/dotc/transform/Constructors.scala | 179 +++++++++++------- tests/pos/Fileish.scala | 53 ++++++ tests/pos/constrs.scala | 33 ++++ 3 files changed, 193 insertions(+), 72 deletions(-) create mode 100644 tests/pos/Fileish.scala create mode 100644 tests/pos/constrs.scala diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 3d2f5a0d7079..9cb348dfff5e 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -21,6 +21,8 @@ import collection.mutable /** This transform * - moves initializers from body to constructor. * - makes all supercalls explicit + * - also moves private fields that are accessed only from constructor + * into the constructor if possible. */ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform => import tpd._ @@ -35,7 +37,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor */ override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { def ownerBecomesConstructor(owner: Symbol): Boolean = - (owner.isLocalDummy || owner.isTerm && !owner.is(Method)) && + (owner.isLocalDummy || owner.isTerm && !owner.is(Method | Lazy)) && owner.owner.isClass if (ownerBecomesConstructor(sym.owner)) sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor) @@ -48,38 +50,14 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) = tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall) - /** Adjustments performed when moving code into the constructor: - * (1) Replace references to param accessors by constructor parameters - * except possibly references to mutable variables, if `excluded = Mutable`. - * (Mutable parameters should be replaced only during the super call) - * (2) If the parameter accessor reference was to an alias getter, - * drop the () when replacing by the parameter. + /** Class members that can be eliminated if referenced only from their own + * constructor. */ - class IntoConstrMap(accessors: List[Symbol], params: List[Symbol]) extends TreeMap { - private var excluded: FlagSet = _ - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Ident(_) | Select(This(_), _) => - val sym = tree.symbol - if (sym is (ParamAccessor, butNot = excluded)) { - val param = sym.subst(accessors, params) - if (param ne sym) ref(param).withPos(tree.pos) - else tree - } - else tree - case Apply(fn, Nil) => - val fn1 = transform(fn) - if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor) - fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass - else cpy.Apply(tree)(fn1, Nil) - case _ => - if (noDirectRefsFrom(tree)) tree else super.transform(tree) - } + private def mightBeDropped(sym: Symbol)(implicit ctx: Context) = + sym.is(Private, butNot = KeeperFlags) && !sym.is(MutableParamAccessor) - def apply(tree: Tree, excluded: FlagSet)(implicit ctx: Context): Tree = { - this.excluded = excluded - transform(tree) - } - } + private final val KeeperFlags = Method | Lazy | NotJavaPrivate + private final val MutableParamAccessor = allOf(Mutable, ParamAccessor) override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { val cls = ctx.owner.asClass @@ -102,7 +80,34 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor } val paramSyms = vparamsWithOuter map (_.symbol) - val intoConstr = new IntoConstrMap(accessors, paramSyms) + // Adjustments performed when moving code into the constructor: + // (1) Replace references to param accessors by constructor parameters + // except possibly references to mutable variables, if `excluded = Mutable`. + // (Mutable parameters should be replaced only during the super call) + // (2) If the parameter accessor reference was to an alias getter, + // drop the () when replacing by the parameter. + object intoConstr extends TreeMap { + private var excluded: FlagSet = _ + override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { + case Ident(_) | Select(This(_), _) => + var sym = tree.symbol + if (sym is (ParamAccessor, butNot = excluded)) sym = sym.subst(accessors, paramSyms) + if (sym.owner.isConstructor) ref(sym).withPos(tree.pos) else tree + case Apply(fn, Nil) => + val fn1 = transform(fn) + if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor) + fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass + else cpy.Apply(tree)(fn1, Nil) + case _ => + if (noDirectRefsFrom(tree)) tree else super.transform(tree) + } + + def apply(tree: Tree, inSuperCall: Boolean = false)(implicit ctx: Context): Tree = { + this.excluded = if (inSuperCall) EmptyFlags else Mutable + transform(tree) + } + } + val superCalls = new mutable.ListBuffer[Tree] // If parent is a constructor call, pull out the call into a separate @@ -115,12 +120,12 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor nme.CONSTRUCTOR), superArgs) => val toClass = !superType.symbol.is(Trait) - val mappedArgs = superArgs.map( - intoConstr(_, excluded = if (toClass) Mutable else EmptyFlags)) + val mappedArgs = superArgs.map(intoConstr(_, inSuperCall = toClass)) val receiver = if (toClass) Super(This(cls), tpnme.EMPTY, inConstrCall = true) else This(cls) - superCalls += cpy.Apply(superApp)( + superCalls += + cpy.Apply(superApp)( receiver.withPos(superNew.pos) .select(superSel.symbol).withPos(superSel.pos), mappedArgs) @@ -129,67 +134,97 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor } val parentTypeTrees = tree.parents.map(normalizeParent) + // Collect all private parameter accessors and value definitions that need + // to be retained. There are several reasons why a parameter accessor or + // definition might need to be retained: + // 1. It is accessed after the constructor has finished + // 2. It is accessed before it is defined + // 3. It is accessed on an object other than `this` + // 4. It is a mutable parameter accessor + // 5. It is has a wildcard initializer `_` + object usage extends TreeTraverser { + private var inConstr: Boolean = true + private val seen = mutable.Set[Symbol](accessors: _*) + val retained = mutable.Set[Symbol]() + def dropped: collection.Set[Symbol] = seen -- retained + override def traverse(tree: Tree) = { + val sym = tree.symbol + tree match { + case Ident(_) | Select(This(_), _) if inConstr && seen(tree.symbol) => + // could refer to definition in constructors, so no retention necessary + case tree: RefTree => + if (mightBeDropped(sym)) retained += sym + case _ => + } + if (!noDirectRefsFrom(tree)) traverseChildren(tree) + } + def collect(stats: List[Tree]): Unit = stats foreach { + case stat: ValDef if !stat.symbol.is(Lazy) => + traverse(stat) + if (mightBeDropped(stat.symbol)) + (if (isWildcardStarArg(stat.rhs)) retained else seen) += stat.symbol + case stat: DefTree => + inConstr = false + traverse(stat) + inConstr = true + case stat => + traverse(stat) + } + } + usage.collect(superCalls.toList ++ tree.body) + + def isRetained(acc: Symbol) = !mightBeDropped(acc) || usage.retained(acc) + + val constrStats, clsStats = new mutable.ListBuffer[Tree] + // Split class body into statements that go into constructor and // definitions that are kept as members of the class. - def splitStats(stats: List[Tree]): (List[Tree], List[Tree]) = stats match { + def splitStats(stats: List[Tree]): Unit = stats match { case stat :: stats1 => - val (constrStats, clsStats) = splitStats(stats1) stat match { - case stat @ ValDef(mods, name, tpt, rhs) => - val inits = - if (rhs.isEmpty || isWildcardArg(rhs)) Nil - else Assign(ref(stat.symbol), intoConstr(rhs, excluded = Mutable)) - .withPos(stat.pos) :: Nil - (inits ::: constrStats, cpy.ValDef(stat)(rhs = EmptyTree) :: clsStats) + case stat @ ValDef(mods, name, tpt, rhs) if !stat.symbol.is(Lazy) => + val sym = stat.symbol + if (isRetained(sym)) { + if (!rhs.isEmpty && !isWildcardArg(rhs)) + constrStats += Assign(ref(sym), intoConstr(rhs)).withPos(stat.pos) + clsStats += cpy.ValDef(stat)(rhs = EmptyTree) + } + else if (!rhs.isEmpty) { + sym.copySymDenotation( + initFlags = sym.flags &~ Private, + owner = constr.symbol).installAfter(thisTransform) + constrStats += intoConstr(stat) + } case _: DefTree => - (constrStats, stat :: clsStats) + clsStats += stat case _ => - (intoConstr(stat, excluded = Mutable) :: constrStats, clsStats) + constrStats += intoConstr(stat) } + splitStats(stats1) case Nil => (Nil, Nil) } - val (constrStats, clsStats) = splitStats(tree.body) - - // Collect all private parameter accessors that need to be retained - // because they are accessed after the constructor has finished. - val collectRetained = new TreeAccumulator[Set[Symbol]] { - override def apply(retained: Set[Symbol], tree: Tree) = tree match { - case tree: RefTree => - val sym = tree.symbol - foldOver( - if (sym.is(PrivateParamAccessor) && sym.owner == cls) retained + sym else retained, - tree) - case _ => - if (noDirectRefsFrom(tree)) retained else foldOver(retained, tree) - } - } - val retainedPrivate = collectRetained(collectRetained(Set[Symbol](), constrStats), clsStats) - def isRetained(acc: Symbol) = - (!acc.is(Private) || acc.is(NotJavaPrivate) || retainedPrivate(acc)) + splitStats(tree.body) val accessorFields = accessors.filterNot(_ is Method) - val (retainedAccessors, droppedAccessors) = accessorFields.partition(isRetained) // The initializers for the retained accessors */ - val copyParams = retainedAccessors.map(acc => + val copyParams = accessorFields.filter(isRetained).map(acc => Assign(ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) // Drop accessors that are not retained from class scope - if (droppedAccessors.nonEmpty) { + val dropped = usage.dropped + if (dropped.nonEmpty) { val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error cls.copy( info = clsInfo.derivedClassInfo( - decls = clsInfo.decls.filteredScope(!droppedAccessors.contains(_)))) + decls = clsInfo.decls.filteredScope(!dropped.contains(_)))) } cpy.Template(tree)( constr = cpy.DefDef(constr)( - rhs = Block(superCalls.toList ::: copyParams ::: constrStats, unitLiteral)), + rhs = Block(superCalls.toList ::: copyParams ::: constrStats.toList, unitLiteral)), parents = parentTypeTrees, - body = clsStats filter { - case vdef: ValDef => !droppedAccessors.contains(vdef.symbol) - case _ => true - }) + body = clsStats.toList) } } \ No newline at end of file diff --git a/tests/pos/Fileish.scala b/tests/pos/Fileish.scala new file mode 100644 index 000000000000..d226bb0bdeee --- /dev/null +++ b/tests/pos/Fileish.scala @@ -0,0 +1,53 @@ +// Inspired by the original Fileish, +// testing combinations of lazy and non-lazy vals for their treatment in constructors +package dotty.tools +package io + +import java.io.{ InputStream } +import java.util.jar.JarEntry +import language.postfixOps + +/** A common interface for File-based things and Stream-based things. + * (In particular, io.File and JarEntry.) + */ +class Fileish(val path: Path, val input: () => InputStream) extends Streamable.Chars { + def inputStream() = input() + + def parent = path.parent + def name = path.name + def isSourceFile = path.hasExtension("java", "scala") + + private lazy val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } + lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") + lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." + + override def toString = path.path +} +class Fileish2(val path: Path, val input: () => InputStream) extends Streamable.Chars { + def inputStream() = input() + + def parent = path.parent + def name = path.name + def isSourceFile = path.hasExtension("java", "scala") + + private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } + lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") + lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." + + override def toString = path.path +} + +class Fileish3(val path: Path, val input: () => InputStream) extends Streamable.Chars { + def inputStream() = input() + + def parent = path.parent + def name = path.name + def isSourceFile = path.hasExtension("java", "scala") + + private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } + private val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") + private val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." + + override def toString = path.path +} + diff --git a/tests/pos/constrs.scala b/tests/pos/constrs.scala new file mode 100644 index 000000000000..dc0e1a369305 --- /dev/null +++ b/tests/pos/constrs.scala @@ -0,0 +1,33 @@ +class Foo(x: Int, var y: Int) { + + val z: Int = 0 + + var u: Int = _ + + def f = x + +} + +class Baz(val base: Int) { + +} + + +class Bar(base: Int, byName: => String, local: Int) extends Baz(base + local) { + + def f() = println(base.toString + byName) + +} + +class Rational(n: Int, d: Int) { + def gcd(x: Int, y: Int): Int = ??? + private val x = gcd(n, d) + def numer = n / x + def denom = d / x +} +class Rational2(n: Int, d: Int) { + def gcd(x: Int, y: Int): Int = ??? + private val x = gcd(n, d) + val numer = n / x + val denom = d / x +} From 462eaf1559cf5cc04c6ebf0a469dc272dd6d53a9 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Sat, 11 Oct 2014 08:28:38 +0200 Subject: [PATCH 29/31] Fix errors in SymDenotations.isCachable. TypeVar is a TypeProxy. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 711a76ad50b3..cea9be604048 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1323,9 +1323,9 @@ object SymDenotations { * or whose instances can change, depending on typerstate. */ def isCachable(tp: Type): Boolean = tp match { + case tp: TypeVar => tp.inst.exists && inCache(tp.inst) case tp: TypeProxy => inCache(tp.underlying) case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) - case tp: TypeVar => tp.inst.exists && inCache(tp.inst) case _ => true } From 57c110dcd2463f938d56f2fc785e0f3f55f1f7de Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Sat, 11 Oct 2014 08:42:32 +0200 Subject: [PATCH 30/31] After-rebase fix: weaken owner-chain restriction. Allow to skip labels. --- src/dotty/tools/dotc/transform/TreeChecker.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 5412b66b8bc6..46886afb6f94 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -98,7 +98,7 @@ class TreeChecker { private def checkOwner(tree: untpd.Tree)(implicit ctx: Context): Unit = { def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean = symOwner == ctxOwner || - ctxOwner.isWeakOwner && !(ctxOwner is Method | Lazy | Mutable) && + ctxOwner.isWeakOwner && (!(ctxOwner is Method | Lazy | Mutable) || (ctxOwner is Label)) && ownerMatches(symOwner, ctxOwner.owner) if(!ownerMatches(tree.symbol.owner, ctx.owner)) { assert(ownerMatches(tree.symbol.owner, ctx.owner), From 8cbc5b385483d7831c7f350d6e1d85fdd03682fb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 11 Oct 2014 14:09:08 +0200 Subject: [PATCH 31/31] ElimByName now handles casts to by-name-parameter TermRefs A cast like expr.asInstanceOf[x.type] where x is a by-name parameter has to be mapped to expr.asInstanceOf[x.type].apply() See pos/t296.scala which would otherwise start failing when the pattern matcher is integrated. --- .../tools/dotc/transform/ElimByName.scala | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ElimByName.scala b/src/dotty/tools/dotc/transform/ElimByName.scala index e656c8438958..1d0307398fc3 100644 --- a/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/src/dotty/tools/dotc/transform/ElimByName.scala @@ -76,16 +76,29 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform cpy.Apply(tree)(tree.fun, args1) } - private def becomesFunction(symd: SymDenotation)(implicit ctx: Context) = + /** If denotation had an ExprType before, it now gets a function type */ + private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = (symd is Param) || (symd is (ParamAccessor, butNot = Method)) - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = { - val origDenot = originalDenotation(tree) - if (becomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) + /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ + private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = { + val origDenot = originalDenotation(ftree) + if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) tree.select(defn.Function0_apply).appliedToNone else tree } + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = + applyIfFunction(tree, tree) + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { + case TypeApply(Select(_, nme.asInstanceOf_), arg :: Nil) => + // tree might be of form e.asInstanceOf[x.type] where x becomes a function. + // See pos/t296.scala + applyIfFunction(tree, arg) + case _ => tree + } + def elimByNameParams(tp: Type)(implicit ctx: Context): Type = tp match { case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimByNameParams(tp.resultType)) @@ -102,6 +115,6 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform } def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - if (becomesFunction(sym)) transformParamInfo(tp) + if (exprBecomesFunction(sym)) transformParamInfo(tp) else elimByNameParams(tp) }