From d2670a7e6ddfc3a71f7b38aefb831b684059d468 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 28 Oct 2014 15:20:48 +0100 Subject: [PATCH 01/42] Made LambdaLift capable of having minitransforms run after it. Some changes needed so that Flatten can run after LambdaLift --- .../tools/dotc/transform/LambdaLift.scala | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 196753e82d8e..051c6065eeb8 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -278,20 +278,20 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } } - override def init(implicit ctx: Context, info: TransformerInfo) = { - free.clear() - proxyMap.clear() - called.clear() - calledFromInner.clear() - liftedOwner.clear() - liftedDefs.clear() - assert(ctx.phase == thisTransform) - (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) - computeFreeVars() - computeLiftedOwners() - generateProxies()(ctx.withPhase(thisTransform.next)) - liftLocals()(ctx.withPhase(thisTransform.next)) - } + override def init(implicit ctx: Context, info: TransformerInfo) = + ctx.atPhase(thisTransform) { implicit ctx => + free.clear() + proxyMap.clear() + called.clear() + calledFromInner.clear() + liftedOwner.clear() + liftedDefs.clear() + (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) + computeFreeVars() + computeLiftedOwners() + generateProxies()(ctx.withPhase(thisTransform.next)) + liftLocals()(ctx.withPhase(thisTransform.next)) + } private def currentEnclosure(implicit ctx: Context) = ctx.owner.enclosingMethod.skipConstructor @@ -355,8 +355,12 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } } - private def liftDef(tree: MemberDef)(implicit ctx: Context): Tree = { - liftedDefs(tree.symbol.owner) += rename(tree, tree.symbol.name) + private def liftDef(tree: MemberDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val buf = liftedDefs(tree.symbol.owner) + transformFollowing(rename(tree, tree.symbol.name)) match { + case Thicket(trees) => buf ++= trees + case tree => buf += tree + } EmptyTree } From ad45e2e4b72057499b33a1cb4fbf5eb41ab8651c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 28 Oct 2014 15:21:33 +0100 Subject: [PATCH 02/42] New miniphase: Flatten --- src/dotty/tools/dotc/Compiler.scala | 3 +- src/dotty/tools/dotc/Flatten.scala | 15 ------ .../dotc/{ => transform}/ElimLocals.scala | 0 src/dotty/tools/dotc/transform/Flatten.scala | 49 +++++++++++++++++++ src/dotty/tools/dotc/transform/SymUtils.scala | 7 ++- .../tools/dotc/transform/TreeChecker.scala | 2 +- test/dotc/tests.scala | 2 +- 7 files changed, 59 insertions(+), 19 deletions(-) delete mode 100644 src/dotty/tools/dotc/Flatten.scala rename src/dotty/tools/dotc/{ => transform}/ElimLocals.scala (100%) create mode 100644 src/dotty/tools/dotc/transform/Flatten.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index d141b7488f2a..cb70480ce7c9 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -55,7 +55,8 @@ class Compiler { List(new Erasure), List(new CapturedVars, new Constructors), - List(new LambdaLift) + List(new LambdaLift, + new Flatten) ) var runId = 1 diff --git a/src/dotty/tools/dotc/Flatten.scala b/src/dotty/tools/dotc/Flatten.scala deleted file mode 100644 index d7ccc1ae4e11..000000000000 --- a/src/dotty/tools/dotc/Flatten.scala +++ /dev/null @@ -1,15 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import DenotTransformers.SymTransformer -import Phases.Phase -import Contexts.Context -import SymDenotations.SymDenotation -import TreeTransforms.MiniPhaseTransform - -class Flatten extends MiniPhaseTransform with SymTransformer { thisTransformer => - override def phaseName = "flatten" - - def transformSym(ref: SymDenotation)(implicit ctx: Context) = ??? -} diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/transform/ElimLocals.scala similarity index 100% rename from src/dotty/tools/dotc/ElimLocals.scala rename to src/dotty/tools/dotc/transform/ElimLocals.scala diff --git a/src/dotty/tools/dotc/transform/Flatten.scala b/src/dotty/tools/dotc/transform/Flatten.scala new file mode 100644 index 000000000000..769503f9d6ef --- /dev/null +++ b/src/dotty/tools/dotc/transform/Flatten.scala @@ -0,0 +1,49 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Phases.Phase +import Contexts.Context +import Flags._ +import SymUtils._ +import SymDenotations.SymDenotation +import collection.mutable +import TreeTransforms.MiniPhaseTransform +import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo + +class Flatten extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "flatten" + + def transformSym(ref: SymDenotation)(implicit ctx: Context) = { + if (ref.isClass && !ref.is(Package) && !ref.owner.is(Package)) { + ref.copySymDenotation( + name = ref.flatName, + owner = ref.enclosingPackageClass) + } + else ref + } + + override def treeTransformPhase = thisTransform.next + + private val liftedDefs = new mutable.ListBuffer[Tree] + + private def liftIfNested(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = + if (ctx.owner is Package) tree + else { + liftedDefs += transformFollowing(tree) + EmptyTree + } + + override def transformStats(stats: List[Tree])(implicit ctx: Context, info: TransformerInfo) = + if (ctx.owner is Package) { + val liftedStats = stats ++ liftedDefs + liftedDefs.clear + liftedStats + } + else stats + + override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = + liftIfNested(tree) +} diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index 2875327c4712..ba45d3f04663 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -5,6 +5,7 @@ import core._ import Types._ import Contexts._ import Symbols._ +import SymDenotations._ import Decorators._ import Names._ import StdNames._ @@ -13,7 +14,8 @@ import Flags._ import language.implicitConversions object SymUtils { - implicit def decorateSymUtils(sym: Symbol): SymUtils = new SymUtils(sym) + implicit def decorateSymbol(sym: Symbol): SymUtils = new SymUtils(sym) + implicit def decorateSymDenot(d: SymDenotation): SymUtils = new SymUtils(d.symbol) } /** A decorator that provides methods on symbols @@ -64,4 +66,7 @@ class SymUtils(val self: Symbol) extends AnyVal { def field(implicit ctx: Context): Symbol = self.owner.info.decl(self.asTerm.name.fieldName).suchThat(!_.is(Method)).symbol + + /** `fullName` where `$' is the separator character */ + def flatName(implicit ctx: Context): Name = self.fullNameSeparated('$') } diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index d2f30e7baf78..f6e36a129b6a 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -180,7 +180,7 @@ class TreeChecker { override def typedStats(trees: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { for (tree <- trees) tree match { case tree: untpd.DefTree => checkOwner(tree) - case _: untpd.Thicket => assert(false, "unexpanded thicket in statement sequence") + case _: untpd.Thicket => assert(false, i"unexpanded thicket $tree in statement sequence $trees%\n%") case _ => } super.typedStats(trees, exprOwner) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 71a1600bb7da..ac1fbe735d60 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,7 +15,7 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-Yno-deep-subtypes", - "-Ycheck:patternMatcher,gettersSetters,lambdaLift" + "-Ycheck:patternMatcher,gettersSetters,flatten" ) val twice = List("#runs", "2", "-YnoDoubleBindings") From 252b6d9f3a80bdfc789e2f5b463ee7dc7a10b659 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 28 Oct 2014 16:21:06 +0100 Subject: [PATCH 03/42] Generalize lift behavior between Flatten and LambdaLift To be combinable with follow-up mini-phases the lift operation needs to handle Thickets specially. This commit factors out the behavior from LambdaLift, so that Flatten can do the same thing. --- src/dotty/tools/dotc/ast/Trees.scala | 7 +++++++ src/dotty/tools/dotc/transform/Flatten.scala | 2 +- src/dotty/tools/dotc/transform/LambdaLift.scala | 5 +---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 71026a4499f3..648f19a4fbb2 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -347,6 +347,11 @@ object Trees { s } + /** If this is a thicket, gerform `op` on each of its trees + * otherwise, perform `op` ion tree itself. + */ + def foreachInThicket(op: Tree[T] => Unit): Unit = op(this) + override def toText(printer: Printer) = printer.toText(this) override def hashCode(): Int = System.identityHashCode(this) @@ -809,6 +814,8 @@ object Trees { val newTrees = trees.map(_.withPos(pos)) new Thicket[T](newTrees).asInstanceOf[this.type] } + override def foreachInThicket(op: Tree[T] => Unit): Unit = + trees foreach (_.foreachInThicket(op)) } class EmptyValDef[T >: Untyped] extends ValDef[T]( diff --git a/src/dotty/tools/dotc/transform/Flatten.scala b/src/dotty/tools/dotc/transform/Flatten.scala index 769503f9d6ef..dceefc0bc90b 100644 --- a/src/dotty/tools/dotc/transform/Flatten.scala +++ b/src/dotty/tools/dotc/transform/Flatten.scala @@ -32,7 +32,7 @@ class Flatten extends MiniPhaseTransform with SymTransformer { thisTransform => private def liftIfNested(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = if (ctx.owner is Package) tree else { - liftedDefs += transformFollowing(tree) + transformFollowing(tree).foreachInThicket(liftedDefs += _) EmptyTree } diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 051c6065eeb8..f36ff6247a3a 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -357,10 +357,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this private def liftDef(tree: MemberDef)(implicit ctx: Context, info: TransformerInfo): Tree = { val buf = liftedDefs(tree.symbol.owner) - transformFollowing(rename(tree, tree.symbol.name)) match { - case Thicket(trees) => buf ++= trees - case tree => buf += tree - } + transformFollowing(rename(tree, tree.symbol.name)).foreachInThicket(buf += _) EmptyTree } From 936e83f3617e7dd1b9141cf20b1dc8ec3482df97 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 29 Oct 2014 11:52:43 +0100 Subject: [PATCH 04/42] New phase: RestoreScopes Cleans up after LambdaLift and Flatten. RestoreScopes exhibited a problem (double definition) when compiling Unpickler. The root of the problem was in Applications.scala. The effect was that arguments woulkd be lifted out, but then the argument expression would be used anyway. That caused a closure to be present twice which caused the double def error much later. -Ycheck did not catch it because the two closure expressions were in non-overlapping scopes. --- src/dotty/tools/dotc/Compiler.scala | 3 +- .../tools/dotc/transform/RestoreScopes.scala | 36 +++++++++++++++++++ src/dotty/tools/dotc/typer/Applications.scala | 2 +- test/dotc/tests.scala | 2 +- 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/RestoreScopes.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index cb70480ce7c9..d4fa7e671104 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -56,7 +56,8 @@ class Compiler { List(new CapturedVars, new Constructors), List(new LambdaLift, - new Flatten) + new Flatten, + new RestoreScopes) ) var runId = 1 diff --git a/src/dotty/tools/dotc/transform/RestoreScopes.scala b/src/dotty/tools/dotc/transform/RestoreScopes.scala new file mode 100644 index 000000000000..4a42523266ec --- /dev/null +++ b/src/dotty/tools/dotc/transform/RestoreScopes.scala @@ -0,0 +1,36 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.IdentityDenotTransformer +import Contexts.Context +import Symbols._ +import Scopes._ +import collection.mutable +import TreeTransforms.MiniPhaseTransform +import ast.Trees._ +import TreeTransforms.TransformerInfo + +/** The preceding lambda lift and flatten phases move symbols to different scopes + * and rename them. This miniphase cleans up afterwards and makes sure that all + * class scopes contain the symbols defined in them. + */ +class RestoreScopes extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "restoreScopes" + + override def treeTransformPhase = thisTransform.next + + override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = { + val TypeDef(_, _, Template(constr, _, _, body)) = tree + val restoredDecls = newScope + for (stat <- constr :: body) + if (stat.isInstanceOf[MemberDef] && stat.symbol.exists) + restoredDecls.enter(stat.symbol) + val cinfo = tree.symbol.asClass.classInfo + tree.symbol.copySymDenotation( + info = cinfo.derivedClassInfo( // Dotty deviation: Cannot expand cinfo inline without a type error + decls = restoredDecls: Scope)).installAfter(thisTransform) + tree + } +} diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 035f19028d7c..a237e7781822 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -461,7 +461,7 @@ trait Applications extends Compatibility { self: Typer => val result = { var typedArgs = typedArgBuf.toList - val app0 = cpy.Apply(app)(normalizedFun, typedArgs) + def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = if (!success) app0.withType(ErrorType) else { diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index ac1fbe735d60..a52f79abd0ba 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,7 +15,7 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-Yno-deep-subtypes", - "-Ycheck:patternMatcher,gettersSetters,flatten" + "-Ycheck:patternMatcher,gettersSetters,restoreScopes" ) val twice = List("#runs", "2", "-YnoDoubleBindings") From d907f26da9f0d3625a4c35021993f04a553bd354 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 30 Oct 2014 17:07:40 +0100 Subject: [PATCH 05/42] Bugfix for superaccessors Threw away list of constructed statements. --- src/dotty/tools/dotc/transform/SuperAccessors.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 7cb4c5825b10..b11658efb7b6 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -295,7 +295,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val body1 = forwardParamAccessors(transformStats(impl.body, tree.symbol)) accDefs -= currentClass ownStats ++= body1 - cpy.Template(impl)(body = body1) + cpy.Template(impl)(body = ownStats.toList) } transformTemplate From 250418e830bc7ccacf13cb0d3a9121238d99632a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 30 Oct 2014 17:09:34 +0100 Subject: [PATCH 06/42] New phase: PrivateToStatic Make private methods in traits static, so that we do not need to give a default for them. --- src/dotty/tools/dotc/Compiler.scala | 3 +- src/dotty/tools/dotc/ast/tpd.scala | 2 +- src/dotty/tools/dotc/core/Flags.scala | 3 + .../tools/dotc/printing/RefinedPrinter.scala | 6 +- .../dotc/transform/PrivateToStatic.scala | 90 +++++++++++++++++++ tests/pos/CoderTrait.scala | 63 +++++++++++++ tests/pos/privates.scala | 9 ++ tests/pos/traits.scala | 25 ++++++ 8 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/PrivateToStatic.scala create mode 100644 tests/pos/CoderTrait.scala create mode 100644 tests/pos/privates.scala create mode 100644 tests/pos/traits.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index d4fa7e671104..f4690df08987 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -57,7 +57,8 @@ class Compiler { new Constructors), List(new LambdaLift, new Flatten, - new RestoreScopes) + new RestoreScopes), + List(new PrivateToStatic) ) var runId = 1 diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index d0f64f5a7b24..74ba79176ea8 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -263,7 +263,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case _ => false } - try test + try test || tp.symbol.is(JavaStatic) catch { // See remark in SymDenotations#accessWithin case ex: NotDefinedHere => test(ctx.addMode(Mode.FutureDefsOK)) } diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index e5bf27eaeb00..804f6af1a3f0 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -512,6 +512,9 @@ object Flags { /** Labeled `private` or `final` */ final val PrivateOrFinal = Private | Final + /** A private method */ + final val PrivateMethod = allOf(Private, Method) + /** A type parameter with synthesized name */ final val ExpandedTypeParam = allOf(ExpandedName, TypeParam) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f0d55882403b..e43aaa24fbec 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -36,7 +36,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def nameString(name: Name): String = name.decode.toString override protected def simpleNameString(sym: Symbol): String = - sym.originalName.decode.toString + sym.name.decode.toString override protected def fullNameOwner(sym: Symbol) = { val owner = super.fullNameOwner(sym) @@ -222,7 +222,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "`" ~ toText(id.name) ~ "`" case Ident(name) => tree.typeOpt match { - case tp: NamedType if name != nme.WILDCARD => toTextPrefix(tp.prefix) ~ selectionString(tp) + case tp: NamedType if name != nme.WILDCARD => + val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix + toTextPrefix(pre) ~ selectionString(tp) case _ => toText(name) } case tree @ Select(qual, name) => diff --git a/src/dotty/tools/dotc/transform/PrivateToStatic.scala b/src/dotty/tools/dotc/transform/PrivateToStatic.scala new file mode 100644 index 000000000000..17f17685568f --- /dev/null +++ b/src/dotty/tools/dotc/transform/PrivateToStatic.scala @@ -0,0 +1,90 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Contexts.Context +import Symbols._ +import Scopes._ +import Flags._ +import StdNames._ +import SymDenotations._ +import Types._ +import collection.mutable +import TreeTransforms._ +import Decorators._ +import ast.Trees._ +import TreeTransforms.TransformerInfo + +/** The preceding lambda lift and flatten phases move symbols to different scopes + * and rename them. This miniphase cleans up afterwards and makes sure that all + * class scopes contain the symbols defined in them. + */ +class PrivateToStatic extends MiniPhase with SymTransformer { thisTransform => + import ast.tpd._ + override def phaseName = "privateToStatic" + override def relaxedTyping = true + + private val Immovable = Deferred | Accessor | JavaStatic + + def shouldBeStatic(sd: SymDenotation)(implicit ctx: Context) = + sd.current(ctx.withPhase(thisTransform)).asInstanceOf[SymDenotation] + .is(PrivateMethod, butNot = Immovable) && + (sd.owner.is(Trait) || sd.is(NotJavaPrivate)) + + override def transformSym(sd: SymDenotation)(implicit ctx: Context): SymDenotation = + if (shouldBeStatic(sd)) { + val mt @ MethodType(pnames, ptypes) = sd.info + sd.copySymDenotation( + initFlags = sd.flags | JavaStatic, + info = MethodType(nme.SELF :: pnames, sd.owner.thisType :: ptypes, mt.resultType)) + } + else sd + + val treeTransform = new Transform(NoSymbol) + + class Transform(thisParam: Symbol) extends TreeTransform { + def phase = thisTransform + override def treeTransformPhase = thisTransform.next + + override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = + if (shouldBeStatic(tree.symbol)) { + val selfParam = ctx.newSymbol(tree.symbol, nme.SELF, Param, tree.symbol.owner.thisType, coord = tree.pos) + new Transform(selfParam) + } + else this + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = + if (shouldBeStatic(tree.symbol)) { + val thisParamDef = ValDef(thisParam.asTerm) + val vparams :: Nil = tree.vparamss + cpy.DefDef(tree)( + mods = tree.mods | JavaStatic, + vparamss = (thisParamDef :: vparams) :: Nil) + } + else tree + + override def transformThis(tree: This)(implicit ctx: Context, info: TransformerInfo) = + if (shouldBeStatic(ctx.owner.enclosingMethod)) ref(thisParam).withPos(tree.pos) + else tree + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + tree.fun match { + case fun @ Select(qual, name) if shouldBeStatic(fun.symbol) => + println(i"mapping $tree to ${cpy.Ident(fun)(name)} (${qual :: tree.args}%, %)") + cpy.Apply(tree)(ref(fun.symbol).withPos(fun.pos), qual :: tree.args) + case _ => + tree + } + + override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo) = + tree.meth match { + case meth @ Select(qual, name) if shouldBeStatic(meth.symbol) => + cpy.Closure(tree)( + env = qual :: tree.env, + meth = ref(meth.symbol).withPos(meth.pos)) + case _ => + tree + } + } +} diff --git a/tests/pos/CoderTrait.scala b/tests/pos/CoderTrait.scala new file mode 100644 index 000000000000..1eba60097fae --- /dev/null +++ b/tests/pos/CoderTrait.scala @@ -0,0 +1,63 @@ +import collection.mutable.HashMap + +trait CoderTrait { + + val words: List[String] + + (2 -> "ABC", new ArrowAssoc('3') -> "DEF") + + private val mnemonics = Map( + '2' -> "ABC", '3' -> "DEF", '4' -> "GHI", '5' -> "JKL", + '6' -> "MNO", '7' -> "PQRS", '8' -> "TUV", '9' -> "WXYZ") + + + ('1', "1") match { + case (digit, str) => true + case _ => false + } + + /** Invert the mnemonics map to give a map from chars 'A' ... 'Z' to '2' ... '9' */ + private val charCode0: Map[Char, Char] = mnemonics withFilter { + case (digit, str) => true + case _ => false + } flatMap { x$1 => + x$1 match { + case (digit, str) => str map (ltr => ltr -> digit) + } + } + + private val charCode: Map[Char, Char] = + for ((digit, str) <- mnemonics; ltr <- str) yield ltr -> digit + + /** Maps a word to the digit string it can represent */ + private def wordCode(word: String): String = word map charCode + + /** A map from digit strings to the words that represent them */ + private val wordsForNum: Map[String, List[String]] = + words groupBy wordCode withDefaultValue Nil + + /** All ways to encode a number as a list of words */ + def encode(number: String): Set[List[String]] = + if (number.isEmpty) Set(Nil) + else { + for { + splitPoint <- 1 to number.length + word <- wordsForNum(number take splitPoint) + rest <- encode(number drop splitPoint) + } yield word :: rest + }.toSet + + /** Maps a number to a list of all word phrases that can represent it */ + def translate(number: String): Set[String] = encode(number) map (_ mkString " ") + +} + +object Coder { + def main(args : Array[String]) : Unit = { + val coder = new CoderTrait { + val words = List("Scala", "sobls", "Python", "Ruby", "C", "A", "rocks", "sucks", "works", "Racka") + } +// println(coder.wordsForNum) + println(coder.translate("7225276257")) + } +} diff --git a/tests/pos/privates.scala b/tests/pos/privates.scala new file mode 100644 index 000000000000..edaa10cb6b1a --- /dev/null +++ b/tests/pos/privates.scala @@ -0,0 +1,9 @@ +trait Test { + + private val x = 2 + + private def foo() = x * x + + private def bar() = foo() + +} diff --git a/tests/pos/traits.scala b/tests/pos/traits.scala new file mode 100644 index 000000000000..15310d5a4e60 --- /dev/null +++ b/tests/pos/traits.scala @@ -0,0 +1,25 @@ +trait B { + + val z: Int + +} + +trait T { + + var x = 2 + + private var xp = 2 + + val y = 3 + + private val yp = 3 + + val z = 4 + + x = 4 + + xp = 4 + +} + +class C extends T From 3c55ed80b94781f09a76c9b6e65ed8255e8a0574 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 31 Oct 2014 17:20:50 +0100 Subject: [PATCH 07/42] First version of mixin transform. Needs adaptations in getters/setters before it can be tested. --- src/dotty/tools/dotc/core/NameOps.scala | 6 +- src/dotty/tools/dotc/core/StdNames.scala | 3 +- src/dotty/tools/dotc/transform/Mixin.scala | 206 ++++++++++++++++++ src/dotty/tools/dotc/transform/SymUtils.scala | 15 ++ 4 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/Mixin.scala diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index beb3142d3876..752d9bcea133 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -47,13 +47,15 @@ object NameOps { } } - object SuperAccessorName { - val pre = nme.SUPER_PREFIX + class PrefixNameExtractor(pre: TermName) { def apply(name: TermName): TermName = pre ++ name def unapply(name: TermName): Option[TermName] = if (name startsWith pre) Some(name.drop(pre.length).asTermName) else None } + object SuperAccessorName extends PrefixNameExtractor(nme.SUPER_PREFIX) + object InitializerName extends PrefixNameExtractor(nme.INITIALIZER_PREFIX) + implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { import nme._ diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 99290f0848d0..80b9a7c37c89 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -121,7 +121,8 @@ object StdNames { val SUPER_PREFIX: N = "super$" val TRAIT_SETTER_PREFIX: N = "_setter_$" val WHILE_PREFIX: N = "while$" - val DEFAULT_EXCEPTION_NAME: N = "ex$" + val DEFAULT_EXCEPTION_NAME: N = "ex$" + val INITIALIZER_PREFIX: N = "initial$" // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala new file mode 100644 index 000000000000..2ad6153d6221 --- /dev/null +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -0,0 +1,206 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import NameOps._ +import collection.mutable + +/** This phase performs the following transformations: + * + * 1. (done in `traitDefs`) Map every concrete trait field + * + * val x: T = expr + * + * to the pair of definitions: + * + * val x: T + * protected def initial$x: T = { stats; expr } + * + * where `stats` comprises all statements between either the start of the trait + * or the previous field definition which are not definitions (i.e. are executed for + * their side effects). + * + * 2. (done in `traitDefs`) Make every concrete trait setter + * + * def x_=(y: T) = () + * + * deferred by maping it to + * + * def x_=(y: T) + * + * 3. For a non-trait class C: + * + * For every trait M directly implemented by the class (see SymUtils.mixin), in + * reverse linearization order, add the following definitions to C: + * + * 3.1 (done in `traitInits`) For every concrete trait field ` val x: T` in M, + * in order of textual occurrence: + * + * val x: T = super[M].initial$x + * + * 3.2 (done in `constrCall`) The call: + * + * super[M]. + * + * 3.3 (done in `setters`) For every concrete setter ` def x_=(y: T)` in M: + * + * def x_=(y: T) = () + * + * 3.4 (done in `superAccessors`) For every superAccessor + * ` def super$f[Ts](ps1)...(psN): U` in M: + * + * def super$f[Ts](ps1)...(psN): U = super[S].f[Ts](ps1)...(psN) + * + * where `S` is the superclass of `M` in the linearization of `C`. + * + * 3.5 (done in `methodOverrides`) For every method + * ` def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: + * + * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) + * + * A method in M needs to be disambiguated if it is concrete, not overridden in C, + * and if it overrides another concrete method. + */ +class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "mixin" + + override def treeTransformPhase = thisTransform.next + + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = + if (sym is Trait) { + lazy val newDecls = sym.decls.cloneScope + var hasInit = false + for (d <- sym.decls) + if (d.isTerm && !d.is(MethodOrDeferred)) { + hasInit = true + val initializer = ctx.newSymbol( + sym.symbol, + InitializerName(d.asTerm.name), + Protected | Synthetic | Method, + ExprType(d.info), + coord = d.coord) + newDecls.enter(initializer) + } + if (hasInit) { + val cinfo = sym.asClass.classInfo + sym.copySymDenotation(info = cinfo.derivedClassInfo(decls = newDecls)) + } + else sym + } + else if ((sym.isField || sym.isSetter) && sym.owner.is(Trait) && !sym.is(Deferred)) + sym.copySymDenotation(initFlags = sym.flags | Deferred) + else + sym + + private val MethodOrDeferred = Method | Deferred + private val PrivateOrDeferred = Private | Deferred + + private def traitDefs(cls: ClassSymbol, stats: List[Tree])(implicit ctx: Context): List[Tree] = { + val initBuf = new mutable.ListBuffer[Tree] + stats flatMap { + case stat: ValDef if !stat.rhs.isEmpty => + val vsym = stat.symbol + val isym = vsym.initializer + val rhs = Block(initBuf.toList, stat.rhs.changeOwner(vsym, isym)) + initBuf.clear() + List( + cpy.ValDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), + DefDef(stat.symbol.initializer, rhs)) + case stat: DefDef if stat.symbol.isSetter => + List(cpy.DefDef(stat)(rhs = EmptyTree)) + case stat: DefTree => + List(stat) + case stat => + initBuf += stat + Nil + } + } + + /** Returns the symbol that is accessed by a super-accessor in a mixin composition. + * + * @param base The class in which everything is mixed together + * @param member The symbol statically referred to by the superaccessor in the trait + */ + private def rebindSuper(base: Symbol, acc: Symbol)(implicit ctx: Context): Symbol = { + var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail + var sym: Symbol = NoSymbol + val SuperAccessorName(memberName) = acc.name + ctx.debuglog("starting rebindsuper " + base + " " + memberName + ":" + acc.info + + " " + acc.owner + " " + base.info.baseClasses + "/" + bcs) + while (!bcs.isEmpty && sym == NoSymbol) { + val other = bcs.head.info.nonPrivateDecl(memberName) + if (ctx.settings.debug.value) + ctx.debuglog("rebindsuper " + bcs.head + " " + other + " " + other.info + + " " + other.symbol.is(Deferred)) + sym = other.matchingDenotation(base.thisType, acc.info).symbol + bcs = bcs.tail + } + sym + } + + private def superAccessors(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = + for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) + yield polyDefDef(implementation(cls, superAcc), forwarder(cls, rebindSuper(cls, superAcc))) + + private def traitInits(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = + for (field <- mixin.decls.filter(fld => fld.isField && !wasDeferred(fld)).toList) + yield DefDef(implementation(cls, field), ref(field.initializer).withPos(cls.pos)) + + private def setters(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = + for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) + yield DefDef(implementation(cls, setter), unitLiteral.withPos(cls.pos)) + + private def implementation(cls: ClassSymbol, member: Symbol)(implicit ctx: Context): TermSymbol = + member.copy(owner = cls, flags = member.flags &~ Deferred) + .enteredAfter(thisTransform).asTerm + + private def constrCall(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context) = + mixinSuperRef(cls, mixin.primaryConstructor) + + private def methodOverrides(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = { + def isOverridden(meth: Symbol) = meth.overridingSymbol(cls).is(Method, butNot = Deferred) + def needsDisambiguation(meth: Symbol): Boolean = + meth.is(Method, butNot = PrivateOrDeferred) && + !isOverridden(meth) && + !meth.allOverriddenSymbols.forall(_ is Deferred) + for (meth <- mixin.decls.toList if needsDisambiguation(meth)) + yield polyDefDef(implementation(cls, meth), forwarder(cls, meth)) + } + + private def wasDeferred(sym: Symbol)(implicit ctx: Context) = + ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } + + private def mixinSuperRef(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = + Super(ref(cls), target.owner.name.asTypeName, inConstrCall = false).select(target) + + private def forwarder(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = + (targs: List[Type]) => (vrefss: List[List[Tree]]) => + mixinSuperRef(cls, target).appliedToTypes(targs).appliedToArgss(vrefss).withPos(cls.pos) + + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = tree.symbol.owner.asClass + val stats = tree.body + cpy.Template(tree)(body = + if (cls is Trait) traitDefs(cls, stats) + else + cls.mixins.flatMap { mixin => + assert(mixin is Trait) + traitInits(cls, mixin) ::: + constrCall(cls, mixin) :: + setters(cls, mixin) + superAccessors(cls, mixin) ::: + methodOverrides(cls, mixin) + } ::: stats) + } +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index ba45d3f04663..ffbc2978592a 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -69,4 +69,19 @@ class SymUtils(val self: Symbol) extends AnyVal { /** `fullName` where `$' is the separator character */ def flatName(implicit ctx: Context): Name = self.fullNameSeparated('$') + + /** The traits mixed into this class in linearization order. + * These are all inherited traits that are not also inherited by the superclass + */ + def mixins(implicit ctx: Context): List[ClassSymbol] = { + val cls = self.asClass + val superCls = cls.classInfo.parents.head.symbol + cls.baseClasses.tail.takeWhile(_ ne superCls) + } + + def initializer(implicit ctx: Context): TermSymbol = + self.owner.info.decl(InitializerName(self.asTerm.name)).symbol.asTerm + + def isField(implicit ctx: Context): Boolean = + self.isTerm && !self.is(Method) } From 0aa670ef30c60264337bb2f2b57d34bbcecd22d2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 1 Nov 2014 10:27:21 +0100 Subject: [PATCH 08/42] Adapt GettersSetters to new Mixin scheme. Trait getters are no longer needed. --- src/dotty/tools/dotc/core/NameOps.scala | 13 +---- src/dotty/tools/dotc/core/StdNames.scala | 1 - .../tools/dotc/transform/GettersSetters.scala | 55 +++++++++---------- src/dotty/tools/dotc/transform/Mixin.scala | 8 ++- src/dotty/tools/dotc/transform/SymUtils.scala | 3 +- 5 files changed, 36 insertions(+), 44 deletions(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 752d9bcea133..56f292b8772d 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -70,7 +70,6 @@ object NameOps { def isProtectedAccessorName = name startsWith PROTECTED_PREFIX def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER def isSetterName = name endsWith SETTER_SUFFIX - def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_PREFIX) def isSingletonName = name endsWith SINGLETON_SUFFIX def isModuleClassName = name endsWith MODULE_SUFFIX def isImportName = name startsWith IMPORT @@ -226,9 +225,6 @@ object NameOps { implicit class TermNameDecorator(val name: TermName) extends AnyVal { import nme._ - def traitSetterName: TermName = - nme.TRAIT_SETTER_PREFIX ++ setterName - def setterName: TermName = if (name.isFieldName) name.fieldToGetter.setterName else name ++ SETTER_SUFFIX @@ -242,13 +238,8 @@ object NameOps { else name ++ LOCAL_SUFFIX private def setterToGetter: TermName = { - val p = name.indexOfSlice(TRAIT_SETTER_PREFIX) - if (p >= 0) - (name drop (p + TRAIT_SETTER_PREFIX.length)).asTermName.getterName - else { - assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") - name.take(name.length - SETTER_SUFFIX.length).asTermName - } + assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") + name.take(name.length - SETTER_SUFFIX.length).asTermName } def fieldToGetter: TermName = { diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 80b9a7c37c89..8393eb56f30a 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -119,7 +119,6 @@ object StdNames { val SINGLETON_SUFFIX: N = ".type" val SPECIALIZED_SUFFIX: N = "$sp" val SUPER_PREFIX: N = "super$" - val TRAIT_SETTER_PREFIX: N = "_setter_$" val WHILE_PREFIX: N = "while$" val DEFAULT_EXCEPTION_NAME: N = "ex$" val INITIALIZER_PREFIX: N = "initial$" diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala index 772a63e52a6c..b0f6c15c8496 100644 --- a/src/dotty/tools/dotc/transform/GettersSetters.scala +++ b/src/dotty/tools/dotc/transform/GettersSetters.scala @@ -16,27 +16,39 @@ import NameOps._ import Flags._ import Decorators._ -/** Performs the following rewritings: +/** Performs the following rewritings on fields of classes, where `x_L` is the "local name" of `x`: * * val x: T = e * --> private val x_L: T = e - * def x: T = x_L (if in class) - * --> private notJavaPrivate var x_L: T = e * def x: T = x_L - * private notJavaPrivate def x_=(x: T): Unit = x_L = x (if in trait) + * * var x: T = e + * def x_=(y: T) = () * --> private var x_L: T = e * def x: T = x_L * def x_=(x: T): Unit = x_L = x (if in class or trait) + * * lazy val x: T = e - * --> lazy def x = e + * --> def x: T = e + * + * val x: T + * --> def x: T + * + * var x: T + * --> def x: T + * + * Omitted from the rewritings are + * + * - private[this] fields in non-trait classes + * - fields generated for static modules (TODO: needed?) + * - parameters, static fields, and fields coming from Java * * Furthermore, assignements to mutable vars are replaced by setter calls * * p.x = e * --> p.x_=(e) */ -class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform => + class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ override def phaseName = "gettersSetters" @@ -45,38 +57,34 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { def noGetterNeeded = - d.is(Method | Param | JavaDefined) || + d.is(NoGetterNeeded) || d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || d.is(Module) && d.isStatic || d.isSelfSym - if (d.isTerm && (d.owner.isClass || d.is(Lazy)) && d.info.isValueType && !noGetterNeeded) { + if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { val maybeStable = if (d.isStable) Stable else EmptyFlags - if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}") + //if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}") d.copySymDenotation( initFlags = d.flags | maybeStable | AccessorCreationFlags, info = ExprType(d.info)) } else d } + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic + private val NoFieldNeeded = Lazy | Deferred | ParamAccessor override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { if (tree.symbol is Method) { val getter = tree.symbol.asTerm assert(getter is Accessor) - if (getter.is(Lazy | Deferred | ParamAccessor)) DefDef(getter, tree.rhs) + if (getter is NoFieldNeeded) + DefDef(getter, tree.rhs) else { val inTrait = getter.owner.is(Trait) - val maybePrivate = - if (inTrait) Private | NotJavaPrivate - else if (getter.owner.isClass) Private - else EmptyFlags - val maybeMutable = - if (inTrait || getter.is(Mutable)) Mutable - else EmptyFlags val field = ctx.newSymbol( owner = ctx.owner, name = getter.name.fieldName, - flags = maybePrivate | maybeMutable, + flags = Private | (getter.flags & Mutable), info = getter.info.resultType).enteredAfter(thisTransform) assert(tree.rhs.tpe.exists, tree.show) val fieldDef = @@ -88,16 +96,7 @@ class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransf val rhs = ref(field) assert(rhs.hasType) val getterDef = DefDef(getter, rhs.ensureConforms(getter.info.widen)) - if (!getter.is(Mutable) && inTrait) { // add a setter anyway, will be needed for mixin - val setter = ctx.newSymbol( - owner = ctx.owner, - name = getter.name.traitSetterName, - flags = (getter.flags & AccessFlags) | Accessor | maybePrivate, - info = MethodType(field.info :: Nil, defn.UnitType)).enteredAfter(thisTransform) - val setterDef = DefDef(setter.asTerm, vrefss => Assign(ref(field), vrefss.head.head)) - Thicket(fieldDef, getterDef, setterDef) - } - else Thicket(fieldDef, getterDef) + Thicket(fieldDef, getterDef) } } else tree diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 2ad6153d6221..515ac9c10f38 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -112,13 +112,17 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => case stat: ValDef if !stat.rhs.isEmpty => val vsym = stat.symbol val isym = vsym.initializer - val rhs = Block(initBuf.toList, stat.rhs.changeOwner(vsym, isym)) + val rhs = Block( + initBuf.toList, + stat.rhs.changeOwner(vsym, isym).ensureConforms(isym.info.widen)) initBuf.clear() List( cpy.ValDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), DefDef(stat.symbol.initializer, rhs)) case stat: DefDef if stat.symbol.isSetter => - List(cpy.DefDef(stat)(rhs = EmptyTree)) + List(cpy.DefDef(stat)( + mods = stat.mods | Deferred, + rhs = EmptyTree)) case stat: DefTree => List(stat) case stat => diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index ffbc2978592a..06882f1c614c 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -61,8 +61,7 @@ class SymUtils(val self: Symbol) extends AnyVal { def setter(implicit ctx: Context): Symbol = if (self.isSetter) self - else accessorNamed(self.asTerm.name.setterName) orElse - accessorNamed(self.asTerm.name.traitSetterName) + else accessorNamed(self.asTerm.name.setterName) def field(implicit ctx: Context): Symbol = self.owner.info.decl(self.asTerm.name.fieldName).suchThat(!_.is(Method)).symbol From 3705a9743b4dd1c2a7a5e42d871e41f6381c97dc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 1 Nov 2014 18:32:09 +0100 Subject: [PATCH 09/42] Avoid getting confused because of Scala2 local suffixes When inheriting from Scala2 traits we sometimes encounter names with a space at the end, denoting a local variable. Drop the space because our translation scheme has no room for it. --- src/dotty/tools/dotc/core/NameOps.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 56f292b8772d..299ead580464 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -276,6 +276,9 @@ object NameOps { -1 } + def stripScala2LocalSuffix: TermName = + if (name.endsWith(" ")) name.init.asTermName else name + /** The name of an accessor for protected symbols. */ def protectedAccessorName: TermName = PROTECTED_PREFIX ++ name.unexpandedName() From bb17767b5b9680adb25bf42bc462b4826132aed4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 1 Nov 2014 18:33:56 +0100 Subject: [PATCH 10/42] Improved version of mixin. Now also handles all supercalls. Seems to do the right thing on pos/traits.scala. But does not pass most tests because the sym transformer forces too many things. --- src/dotty/tools/dotc/Compiler.scala | 5 +- src/dotty/tools/dotc/ast/tpd.scala | 4 +- .../tools/dotc/transform/Constructors.scala | 36 ++------ .../tools/dotc/transform/GettersSetters.scala | 1 + src/dotty/tools/dotc/transform/Mixin.scala | 84 +++++++++++++------ src/dotty/tools/dotc/transform/SymUtils.scala | 9 -- src/dotty/tools/dotc/typer/TypeAssigner.scala | 5 +- test/dotc/tests.scala | 2 +- tests/pos/traits.scala | 4 +- 9 files changed, 79 insertions(+), 71 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index f4690df08987..62759ba09f71 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -50,8 +50,9 @@ class Compiler { new Splitter), List(new ElimByName, new InterceptedMethods, - new Literalize, - new GettersSetters), + new Literalize), + List(new Mixin), + List(new GettersSetters), List(new Erasure), List(new CapturedVars, new Constructors), diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 74ba79176ea8..8c300c2317eb 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -39,8 +39,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def This(cls: ClassSymbol)(implicit ctx: Context): This = untpd.This(cls.name).withType(cls.thisType) - def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean)(implicit ctx: Context): Super = - ta.assignType(untpd.Super(qual, mix), qual, inConstrCall) + def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super = + ta.assignType(untpd.Super(qual, mix), qual, inConstrCall, mixinClass) def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = ta.assignType(untpd.Apply(fn, args), fn, args) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 7bde1ba4ffa4..c966e2678b66 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -109,32 +109,6 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor } } - 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(_, inSuperCall = toClass)) - 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) - // 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: @@ -172,7 +146,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor traverse(stat) } } - usage.collect(superCalls.toList ++ tree.body) + usage.collect(tree.body) def isRetained(acc: Symbol) = !mightBeDropped(acc) || usage.retained(acc) @@ -226,10 +200,14 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor decls = clsInfo.decls.filteredScope(!dropped.contains(_)))) } + val (superCalls, followConstrStats) = constrStats.toList match { + case (sc: Apply) :: rest if sc.symbol.isConstructor => (sc :: Nil, rest) + case stats => (Nil, stats) + } + cpy.Template(tree)( constr = cpy.DefDef(constr)( - rhs = Block(superCalls.toList ::: copyParams ::: constrStats.toList, unitLiteral)), - parents = parentTypeTrees, + rhs = Block(superCalls ::: copyParams ::: followConstrStats, unitLiteral)), body = clsStats.toList) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala index b0f6c15c8496..b5933cc4835e 100644 --- a/src/dotty/tools/dotc/transform/GettersSetters.scala +++ b/src/dotty/tools/dotc/transform/GettersSetters.scala @@ -105,6 +105,7 @@ import Decorators._ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor)) { val Literal(Constant(())) = tree.rhs + assert(tree.symbol.field.exists, i"no field for ${tree.symbol.showLocated}") val initializer = Assign(ref(tree.symbol.field), ref(tree.vparamss.head.head.symbol)) assert(initializer.hasType) cpy.DefDef(tree)(rhs = initializer) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 515ac9c10f38..8116c723a562 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -11,7 +11,10 @@ import SymDenotations._ import Types._ import Decorators._ import DenotTransformers._ +import StdNames._ import NameOps._ +import ast.Trees._ +import util.Positions._ import collection.mutable /** This phase performs the following transformations: @@ -155,22 +158,21 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => private def superAccessors(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) - yield polyDefDef(implementation(cls, superAcc), forwarder(cls, rebindSuper(cls, superAcc))) + yield polyDefDef(implementation(cls, superAcc.asTerm), forwarder(cls, rebindSuper(cls, superAcc))) private def traitInits(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = for (field <- mixin.decls.filter(fld => fld.isField && !wasDeferred(fld)).toList) - yield DefDef(implementation(cls, field), ref(field.initializer).withPos(cls.pos)) + yield ValDef(implementation(cls, field.asTerm), superRef(cls, field.initializer, cls.pos)) private def setters(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) - yield DefDef(implementation(cls, setter), unitLiteral.withPos(cls.pos)) + yield DefDef(implementation(cls, setter.asTerm), unitLiteral.withPos(cls.pos)) - private def implementation(cls: ClassSymbol, member: Symbol)(implicit ctx: Context): TermSymbol = - member.copy(owner = cls, flags = member.flags &~ Deferred) - .enteredAfter(thisTransform).asTerm - - private def constrCall(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context) = - mixinSuperRef(cls, mixin.primaryConstructor) + private def implementation(cls: ClassSymbol, member: TermSymbol)(implicit ctx: Context): TermSymbol = + member.copy( + owner = cls, + name = member.name.stripScala2LocalSuffix, + flags = member.flags &~ Deferred).enteredAfter(thisTransform).asTerm private def methodOverrides(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = { def isOverridden(meth: Symbol) = meth.overridingSymbol(cls).is(Method, butNot = Deferred) @@ -179,32 +181,66 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => !isOverridden(meth) && !meth.allOverriddenSymbols.forall(_ is Deferred) for (meth <- mixin.decls.toList if needsDisambiguation(meth)) - yield polyDefDef(implementation(cls, meth), forwarder(cls, meth)) + yield polyDefDef(implementation(cls, meth.asTerm), forwarder(cls, meth)) } private def wasDeferred(sym: Symbol)(implicit ctx: Context) = ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } - private def mixinSuperRef(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = - Super(ref(cls), target.owner.name.asTypeName, inConstrCall = false).select(target) + private def superRef(cls: ClassSymbol, target: Symbol, pos: Position)(implicit ctx: Context) = { + val inTrait = target.owner is Trait + Super( + qual = This(cls), + mix = if (inTrait) target.owner.name.asTypeName else tpnme.EMPTY, + inConstrCall = target.isConstructor && !target.owner.is(Trait), + mixinClass = if (inTrait) target.owner else NoSymbol + ).select(target) + } private def forwarder(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = (targs: List[Type]) => (vrefss: List[List[Tree]]) => - mixinSuperRef(cls, target).appliedToTypes(targs).appliedToArgss(vrefss).withPos(cls.pos) + superRef(cls, target, cls.pos).appliedToTypes(targs).appliedToArgss(vrefss) override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { val cls = tree.symbol.owner.asClass val stats = tree.body - cpy.Template(tree)(body = - if (cls is Trait) traitDefs(cls, stats) - else - cls.mixins.flatMap { mixin => - assert(mixin is Trait) - traitInits(cls, mixin) ::: - constrCall(cls, mixin) :: - setters(cls, mixin) - superAccessors(cls, mixin) ::: - methodOverrides(cls, mixin) - } ::: stats) + val superCls = cls.classInfo.parents.head.symbol + val mixins = cls.baseClasses.tail.takeWhile(_ ne superCls) + + // If parent is a constructor call, pull out the call into a separate + // supercall constructor, which gets added to `superCalls`, and keep + // only the type. + val superCalls = new mutable.HashMap[Symbol, Tree] + def normalizeParent(tree: Tree) = tree match { + case superApp @ Apply(superSel @ Select(New(superType), nme.CONSTRUCTOR), superArgs) => + val constr = superSel.symbol + superCalls(constr.owner) = superRef(cls, constr, superSel.pos).appliedToArgs(superArgs) + superType + case tree: TypeTree => tree + } + val parentTypeTrees = tree.parents.map(normalizeParent) + + def supCalls(baseCls: Symbol): List[Tree] = superCalls.remove(baseCls) match { + case Some(call) => call :: Nil + case None => + if (baseCls is Interface) Nil + else superRef(cls, baseCls.primaryConstructor, cls.pos).appliedToNone :: Nil + } + + cpy.Template(tree)( + parents = parentTypeTrees, + body = + if (cls is Trait) traitDefs(cls, stats) + else { + val mixInits = mixins.flatMap { mixin => + assert(mixin is Trait) + traitInits(cls, mixin) ::: + supCalls(mixin) ::: + setters(cls, mixin) ::: + superAccessors(cls, mixin) ::: + methodOverrides(cls, mixin) + } + supCalls(superCls) ::: mixInits ::: stats + }) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index 06882f1c614c..449affb9e3d3 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -69,15 +69,6 @@ class SymUtils(val self: Symbol) extends AnyVal { /** `fullName` where `$' is the separator character */ def flatName(implicit ctx: Context): Name = self.fullNameSeparated('$') - /** The traits mixed into this class in linearization order. - * These are all inherited traits that are not also inherited by the superclass - */ - def mixins(implicit ctx: Context): List[ClassSymbol] = { - val cls = self.asClass - val superCls = cls.classInfo.parents.head.symbol - cls.baseClasses.tail.takeWhile(_ ne superCls) - } - def initializer(implicit ctx: Context): TermSymbol = self.owner.info.decl(InitializerName(self.asTerm.name)).symbol.asTerm diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index bb488bdc5c82..ed10c764454a 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -236,7 +236,7 @@ trait TypeAssigner { tree.withType(cls.thisType) } - def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean)(implicit ctx: Context) = { + def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = { val mix = tree.mix val cls = qual.tpe.widen.typeSymbol @@ -249,7 +249,8 @@ trait TypeAssigner { errorType("ambiguous parent class qualifier", tree.pos) } val owntype = - if (!mix.isEmpty) findMixinSuper(cls.info) + if (mixinClass.exists) mixinClass.typeRef + else if (!mix.isEmpty) findMixinSuper(cls.info) else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent else { val ps = cls.info.parents diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index a52f79abd0ba..2b0d7eb0837c 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,7 +15,7 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-Yno-deep-subtypes", - "-Ycheck:patternMatcher,gettersSetters,restoreScopes" + "-Ycheck:patternMatcher,mixin,gettersSetters,restoreScopes" ) val twice = List("#runs", "2", "-YnoDoubleBindings") diff --git a/tests/pos/traits.scala b/tests/pos/traits.scala index 15310d5a4e60..db611eeb5ff2 100644 --- a/tests/pos/traits.scala +++ b/tests/pos/traits.scala @@ -1,10 +1,10 @@ -trait B { +trait B extends Object { val z: Int } -trait T { +trait T extends B { var x = 2 From 73f8b43e9a76cd2961e51cbaa62111db1654e743 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 10:27:39 +0100 Subject: [PATCH 11/42] New option -Ydebug-owners Will print owners of symbols when displaying trees. Requires -Yprint-syms to be set also. --- src/dotty/tools/dotc/config/ScalaSettings.scala | 5 +++-- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index 743925e40753..abde6cb5317f 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -107,8 +107,9 @@ class ScalaSettings extends Settings.SettingGroup { val Xdce = BooleanSetting("-Ydead-code", "Perform dead code elimination.") val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") val debugNames = BooleanSetting("-YdebugNames", "Show name-space indicators when printing names") - val debugTrace = BooleanSetting("-YdebugTrace", "Trace core operations") - val debugFlags = BooleanSetting("-YdebugFlags", "Print all flags of definitions") + val debugTrace = BooleanSetting("-Ydebug-trace", "Trace core operations") + val debugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions") + val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)") //val doc = BooleanSetting ("-Ydoc", "Generate documentation") val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val inline = BooleanSetting("-Yinline", "Perform inlining when possible.") diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index e43aaa24fbec..50b73a35772e 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -205,7 +205,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } def dclTextOr(treeText: => Text) = - if (useSymbol) annotsText(tree.symbol) ~~ dclText(tree.symbol) + if (useSymbol) + annotsText(tree.symbol) ~~ dclText(tree.symbol) ~ + ( " " provided ctx.settings.debugOwners.value) else treeText def idText(tree: untpd.Tree): Text = { From d9057eddf68b5009b6fdee816a71f899f3f90439 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 10:30:13 +0100 Subject: [PATCH 12/42] Make cloneScope less forcefull. Motivation: Avoid needless forcing of symbols in scope. This is a problem when cloneScope is called in TreeTransforms. --- src/dotty/tools/dotc/core/Scopes.scala | 31 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 494a26f7e58d..09bdca19625a 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -20,7 +20,7 @@ import printing.Printer import util.common._ import util.DotClass import SymDenotations.NoDenotation -import collection.mutable.ListBuffer +import collection.mutable object Scopes { @@ -172,12 +172,27 @@ object Scopes { */ private var elemsCache: List[Symbol] = null - def cloneScope(implicit ctx: Context): MutableScope = newScopeWith(this.toList: _*) + /** Clone scope, taking care not to force the denotations of any symbols in the scope. + */ + def cloneScope(implicit ctx: Context): MutableScope = { + val entries = new mutable.ArrayBuffer[ScopeEntry] + var e = lastEntry + while ((e ne null) && e.owner == this) { + entries += e + e = e.prev + } + val scope = newScope + for (i <- entries.length - 1 to 0 by -1) { + val e = entries(i) + scope.newScopeEntry(e.name, e.sym) + } + scope + } - /** create and enter a scope entry */ - protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = { + /** create and enter a scope entry with given name and symbol */ + protected def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = { ensureCapacity(if (hashTable ne null) hashTable.length else MinHash) - val e = new ScopeEntry(sym.name, sym, this) + val e = new ScopeEntry(name, sym, this) e.prev = lastEntry lastEntry = e if (hashTable ne null) enterInHash(e) @@ -186,6 +201,10 @@ object Scopes { e } + /** create and enter a scope entry */ + protected def newScopeEntry(sym: Symbol)(implicit ctx: Context): ScopeEntry = + newScopeEntry(sym.name, sym) + private def enterInHash(e: ScopeEntry)(implicit ctx: Context): Unit = { val idx = e.name.hashCode & (hashTable.length - 1) e.tail = hashTable(idx) @@ -325,7 +344,7 @@ object Scopes { } override def implicitDecls(implicit ctx: Context): List[TermRef] = { - var irefs = new ListBuffer[TermRef] + var irefs = new mutable.ListBuffer[TermRef] var e = lastEntry while (e ne null) { if (e.sym is Implicit) { From 6676cf908fe40f0194124d8bfada417c2a0e18d7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 10:31:24 +0100 Subject: [PATCH 13/42] Fix problem in changeOwner The problem manifests itself when changing the owner of a class field. Being a ValDef, this is classified as a weak owner. But it's parent (the class) should not be owner-changed. --- src/dotty/tools/dotc/ast/tpd.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 8c300c2317eb..6aa715c8ed4e 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -527,7 +527,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { */ def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = { def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = { - if (from.isWeakOwner) loop(from.owner, from :: froms, to :: tos) + if (from.isWeakOwner && !from.owner.isClass) + loop(from.owner, from :: froms, to :: tos) else { //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree) From e9eb0c33b25e460104ee959882c584f0e1e17d3d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 11:14:38 +0100 Subject: [PATCH 14/42] Adaptation of Constructors to new mixin scheme. No more trait_setters are called. --- src/dotty/tools/dotc/transform/Constructors.scala | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index c966e2678b66..9420ce2c089a 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -152,10 +152,6 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val constrStats, clsStats = new mutable.ListBuffer[Tree] - def assign(vble: Symbol, rhs: Tree): Tree = - if (cls is Trait) ref(vble.setter).appliedTo(rhs) - else Assign(ref(vble), rhs) - // Split class body into statements that go into constructor and // definitions that are kept as members of the class. def splitStats(stats: List[Tree]): Unit = stats match { @@ -165,7 +161,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val sym = stat.symbol if (isRetained(sym)) { if (!rhs.isEmpty && !isWildcardArg(rhs)) - constrStats += assign(sym, intoConstr(rhs)).withPos(stat.pos) + constrStats += Assign(ref(sym), intoConstr(rhs)).withPos(stat.pos) clsStats += cpy.ValDef(stat)(rhs = EmptyTree) } else if (!rhs.isEmpty) { @@ -189,7 +185,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor // The initializers for the retained accessors */ val copyParams = accessorFields.filter(isRetained).map(acc => - assign(acc, ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) + Assign(ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) // Drop accessors that are not retained from class scope val dropped = usage.dropped From 3db649fbb5b4ecb714bbd75cec375a3d4bb2d7df Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Nov 2014 11:16:20 +0100 Subject: [PATCH 15/42] Adaptation of explicitOuter to trait initializers Trait initializers do not have outer params; need to avoid passing outer args to them. This problem did not manifest itself before because no trait constructor calls were generated before erasure. --- src/dotty/tools/dotc/transform/ExplicitOuter.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index d056d7e351a2..3bef4e26353f 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -187,6 +187,10 @@ object ExplicitOuter { private def hasOuter(cls: ClassSymbol)(implicit ctx: Context): Boolean = needsOuterIfReferenced(cls) && outerAccessor(cls).exists + /** Class constructor takes an outer argument. Can be called only after phase ExplicitOuter. */ + private def hasOuterParam(cls: ClassSymbol)(implicit ctx: Context): Boolean = + !cls.is(Trait) && needsOuterIfReferenced(cls) && outerAccessor(cls).exists + /** Tree references a an outer class of `cls` which is not a static owner. */ def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = { @@ -248,7 +252,7 @@ object ExplicitOuter { /** If `cls` has an outer parameter add one to the method type `tp`. */ def addParam(cls: ClassSymbol, tp: Type): Type = - if (hasOuter(cls)) { + if (hasOuterParam(cls)) { val mt @ MethodType(pnames, ptypes) = tp mt.derivedMethodType( nme.OUTER :: pnames, cls.owner.enclosingClass.typeRef :: ptypes, mt.resultType) @@ -264,11 +268,11 @@ object ExplicitOuter { case New(tpt) => singleton(outerPrefix(tpt.tpe)) case This(_) => - ref(outerParamAccessor(cls)) // will be rewried to outer argument of secondary constructor in phase Constructors + ref(outerParamAccessor(cls)) // will be rewired to outer argument of secondary constructor in phase Constructors case TypeApply(Select(r, nme.asInstanceOf_), args) => outerArg(r) // cast was inserted, skip } - if (hasOuter(cls)) + if (hasOuterParam(cls)) methPart(fun) match { case Select(receiver, _) => outerArg(receiver).withPos(fun.pos) :: Nil } From 746957d64b5bd14fee8107a73e759ea34ff8126c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 09:30:36 +0100 Subject: [PATCH 16/42] Take supercalls into account for statement context After Mixin, super calls can appear in statemenr sequences. They need to be typechecked and transformed using a special context. --- src/dotty/tools/dotc/core/Contexts.scala | 13 +++++++++---- src/dotty/tools/dotc/transform/MacroTransform.scala | 3 +-- src/dotty/tools/dotc/transform/TreeTransform.scala | 3 +-- src/dotty/tools/dotc/typer/Typer.scala | 3 +-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index de6b0cabfde7..6293d18d2d10 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -297,16 +297,21 @@ object Contexts { def thisCallArgContext: Context = { assert(owner.isClassConstructor) val constrCtx = outersIterator.dropWhile(_.outer.owner == owner).next - var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next - classCtx.superOrThisCallContext(owner, constrCtx.scope).setTyperState(typerState) + superOrThisCallContext(owner, constrCtx.scope).setTyperState(typerState) } /** The super= or this-call context with given owner and locals. */ private def superOrThisCallContext(owner: Symbol, locals: Scope): FreshContext = { - assert(isClassDefContext) - outer.fresh.setOwner(owner).setScope(locals).setMode(ctx.mode | Mode.InSuperCall) + var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next + classCtx.outer.fresh.setOwner(owner).setScope(locals).setMode(classCtx.mode | Mode.InSuperCall) } + /** The context of expression `expr` seen as a member of a statement sequence */ + def exprContext(stat: Tree[_ >: Untyped], exprOwner: Symbol) = + if (exprOwner == this.owner) this + else if (untpd.isSuperConstrCall(stat) && this.owner.isClass) superCallContext + else ctx.fresh.setOwner(exprOwner) + /** The current source file; will be derived from current * compilation unit. */ diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 47ffaafb3e0d..3a8bcc92071c 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -44,11 +44,10 @@ abstract class MacroTransform extends Phase { def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat) case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat)(exprCtx) + case _ => transform(stat)(ctx.exprContext(stat, exprOwner)) } flatten(trees.mapconserve(transformStat(_))) } diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 588a13fc93a1..2037bede8553 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -1200,11 +1200,10 @@ object TreeTransforms { def transformStats(trees: List[Tree], exprOwner: Symbol, info: TransformerInfo, current: Int)(implicit ctx: Context): List[Tree] = { val newInfo = mutateTransformers(info, prepForStats, info.nx.nxPrepStats, trees, current) - val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat, newInfo, current) case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) - case _ => transform(stat, newInfo, current)(exprCtx) + case _ => transform(stat, newInfo, current)(ctx.exprContext(stat, exprOwner)) } val newTrees = flatten(trees.mapconserve(transformStat)) goStats(newTrees, newInfo.nx.nxTransStats(current))(ctx, newInfo) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 3c36a1f256ef..59fda174df1e 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1042,8 +1042,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case Thicket(stats) :: rest => traverse(stats ++ rest) case stat :: rest => - val nestedCtx = if (exprOwner == ctx.owner) ctx else ctx.fresh.setOwner(exprOwner) - buf += typed(stat)(nestedCtx) + buf += typed(stat)(ctx.exprContext(stat, exprOwner)) traverse(rest) case nil => buf.toList From 582506e02cd73eb054e3f029e67b7a1b56bc9907 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:14:05 +0100 Subject: [PATCH 17/42] Fixed redundancy in membersNeedAsSeenFrom Array types are no longer used after erasure, so the test was redundant. --- 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 ae8fceeb743b..99b1c9f7788b 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -560,7 +560,7 @@ object SymDenotations { def membersNeedAsSeenFrom(pre: Type)(implicit ctx: Context) = !( this.isTerm || this.isStaticOwner - || ctx.erasedTypes && symbol != defn.ArrayClass + || ctx.erasedTypes || (pre eq NoPrefix) || (pre eq thisType) ) From 17f3ac7cfdb224fbebc59e450faa9bda711c0573 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:15:05 +0100 Subject: [PATCH 18/42] Fixed typing of supertypes. Previous version could fail where the thisType widened to an explicit self type. --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index ed10c764454a..765c6bea7f41 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -238,7 +238,8 @@ trait TypeAssigner { def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = { val mix = tree.mix - val cls = qual.tpe.widen.typeSymbol + val qtype @ ThisType(_) = qual.tpe + val cls = qtype.cls def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix) match { case p :: Nil => From ddff5921e4387bc2f9c252af64950316312aac1c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:16:48 +0100 Subject: [PATCH 19/42] Fix of computeDenot. Since we demand that after erasure all TermRefs are SymDenotations we need to assure this when computing the denotations of term refs in new phases. --- src/dotty/tools/dotc/core/Types.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d0ddfdd28b50..6dce5581de8f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1173,8 +1173,12 @@ object Types { if (newd.exists) newd else d.staleSymbolError } case d => - if (d.validFor.runId == ctx.period.runId) d.current - else loadDenot + if (d.validFor.runId != ctx.period.runId) + loadDenot + else if (ctx.erasedTypes && lastSymbol != null) + denotOfSym(lastSymbol) // avoid keeping non-sym denotations after erasure; they violate the assertErased contract + else + d.current } if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot") else if (d.exists) { @@ -1623,8 +1627,10 @@ object Types { final class CachedSuperType(thistpe: Type, supertpe: Type) extends SuperType(thistpe, supertpe) object SuperType { - def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = + def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = { + assert(thistpe != NoPrefix) unique(new CachedSuperType(thistpe, supertpe)) + } } /** A constant type with single `value`. */ @@ -1817,7 +1823,7 @@ object Types { } catch { case ex: AssertionError => - println(i"failure while taking result signture of $resultType") + println(i"failure while taking result signture of $this: $resultType") throw ex } From fba1181f59ee7ffff9175067625041f1152bc138 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Nov 2014 20:19:06 +0100 Subject: [PATCH 20/42] Select operations in erasure should fix symbols in types. Otherwise we get binding races, e.g. for pos/t2133.scala after mixin because we create same-named symbols in subclasses, and type refs now can refer to the subclass symbol where previously they refereed to the superclass symbol. To avoid a data race the new term ref will have a fixed symbol. --- 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 9f381bb8e7d9..fd90481572ff 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -277,7 +277,7 @@ object Erasure extends TypeTestsCasts{ case _ => sym.name } untpd.cpy.Select(tree)(qual, sym.name) - .withType(NamedType.withSymAndName(qual.tpe, sym, name)) + .withType(NamedType.withFixedSym(qual.tpe, sym)) } def selectArrayMember(qual: Tree, erasedPre: Type): Tree = From 1ff9e63e003bffaff2e0bafee6ddca146635474f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Nov 2014 11:02:23 +0100 Subject: [PATCH 21/42] Superaccessors are methods Need to have Method flag set --- src/dotty/tools/dotc/transform/SuperAccessors.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index b11658efb7b6..537c8c0c6a0f 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -90,7 +90,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val superAcc = clazz.info.decl(supername).suchThat(_.signature == sym.signature).symbol orElse { ctx.debuglog(s"add super acc ${sym.showLocated} to $clazz") val acc = ctx.newSymbol( - clazz, supername, SuperAccessor | Private | Artifact, + clazz, supername, SuperAccessor | Private | Artifact | Method, ensureMethodic(sel.tpe.widenSingleton), coord = sym.coord).enteredAfter(thisTransformer) // Diagnostic for SI-7091 if (!accDefs.contains(clazz)) From 4a753a1f97b2da0e49a8b6179e5fe778aacbc278 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 6 Nov 2014 10:41:08 +0100 Subject: [PATCH 22/42] Cleanup of code for ExplicitOuter --- src/dotty/tools/dotc/transform/ExplicitOuter.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 3bef4e26353f..28d742b5ee27 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -265,8 +265,8 @@ object ExplicitOuter { if (fun.symbol.isConstructor) { val cls = fun.symbol.owner.asClass def outerArg(receiver: Tree): Tree = receiver match { - case New(tpt) => - singleton(outerPrefix(tpt.tpe)) + case New(_) | Super(_, _) => + singleton(outerPrefix(receiver.tpe)) case This(_) => ref(outerParamAccessor(cls)) // will be rewired to outer argument of secondary constructor in phase Constructors case TypeApply(Select(r, nme.asInstanceOf_), args) => From b04854a37f843b889cc89b3fa9cce4009154f55f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Nov 2014 22:11:40 +0100 Subject: [PATCH 23/42] Avoid noisy print statement in PrivateToStatic --- src/dotty/tools/dotc/transform/PrivateToStatic.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/PrivateToStatic.scala b/src/dotty/tools/dotc/transform/PrivateToStatic.scala index 17f17685568f..b9bfff459866 100644 --- a/src/dotty/tools/dotc/transform/PrivateToStatic.scala +++ b/src/dotty/tools/dotc/transform/PrivateToStatic.scala @@ -71,7 +71,7 @@ class PrivateToStatic extends MiniPhase with SymTransformer { thisTransform => override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = tree.fun match { case fun @ Select(qual, name) if shouldBeStatic(fun.symbol) => - println(i"mapping $tree to ${cpy.Ident(fun)(name)} (${qual :: tree.args}%, %)") + //println(i"mapping $tree to ${cpy.Ident(fun)(name)} (${qual :: tree.args}%, %)") cpy.Apply(tree)(ref(fun.symbol).withPos(fun.pos), qual :: tree.args) case _ => tree From afded4a191dbaab87a0b69850ba92ba13dbae6e9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Nov 2014 22:12:01 +0100 Subject: [PATCH 24/42] Methods always shwoDcl as `def` This is reasonable because getters are only generated late in the compilation pipeline. No need to hide a getter as a val in error messages. --- src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 9fba7ec09816..eace75433cfd 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -303,7 +303,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (flags is Mutable) "var" else if (flags is Package) "package" else if (flags is Module) "object" - else if (sym.isSourceMethod) "def" + else if (sym is Method) "def" else if (sym.isTerm && (!(flags is Param))) "val" else "" } From 63b917955ffdbac7a997c7b71812e898050a1f1d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:39:35 +0100 Subject: [PATCH 25/42] Fixes to Unit handling in erasure 1. Erase unit results in getters to BoxedUnit. 2. Erase => Unit to ()Unit; was ()BoxedUnit 3. Make sure ValDefs have same type in tpt as in symbol, analogous to DefDefs. --- src/dotty/tools/dotc/TypeErasure.scala | 12 +++++++++--- src/dotty/tools/dotc/transform/Erasure.scala | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 2a55d6732f83..448dd3f57a6a 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -4,6 +4,7 @@ package core import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined import dotc.transform.ExplicitOuter._ +import typer.Mode import util.DotClass /** Erased types are: @@ -89,7 +90,7 @@ object TypeErasure { /** The current context with a phase no later than erasure */ private def erasureCtx(implicit ctx: Context) = - if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase) else ctx + if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase).addMode(Mode.FutureDefsOK) else ctx def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)(erasureCtx) def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)(erasureCtx) @@ -141,7 +142,12 @@ object TypeErasure { if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) else if (sym.isAbstractType) TypeAlias(WildcardType) else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) - else eraseInfo(tp)(erasureCtx) + else eraseInfo(tp)(erasureCtx) match { + case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => + defn.BoxedUnitClass.typeRef + case einfo => + einfo + } } def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !( @@ -319,7 +325,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild } def eraseInfo(tp: Type)(implicit ctx: Context) = tp match { - case ExprType(rt) => MethodType(Nil, Nil, erasure(rt)) + case ExprType(rt) => MethodType(Nil, Nil, eraseResult(rt)) case tp => erasure(tp) } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index fd90481572ff..b51a2360e28e 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -247,6 +247,10 @@ object Erasure extends TypeTestsCasts{ tree.withType(erased) } + override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = + if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt) + else super.typedLiteral(tree) + /** Type check select nodes, applying the following rewritings exhaustively * on selections `e.m`, where `OT` is the type of the owner of `m` and `ET` * is the erased type of the selection's original qualifier expression. @@ -369,12 +373,20 @@ object Erasure extends TypeTestsCasts{ } } + override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context): ValDef = + super.typedValDef(untpd.cpy.ValDef(vdef)( + tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym) + override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { + val restpe = sym.info.resultType val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, vparamss = ddef.vparamss.flatten :: Nil, - tpt = // keep UnitTypes intact in result position - untpd.TypedSplice(TypeTree(eraseResult(ddef.tpt.typeOpt)).withPos(ddef.tpt.pos))) + tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), + rhs = ddef.rhs match { + case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) + case _ => ddef.rhs + }) super.typedDefDef(ddef1, sym) } From 1995bf6392ce4907f5c464e9375fe5f30bdcf3cd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:40:19 +0100 Subject: [PATCH 26/42] More systematic handling of Scala2LocalSuffix. Separate test instead of inline. --- src/dotty/tools/dotc/core/NameOps.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 299ead580464..bc15f6a066bc 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -76,7 +76,7 @@ object NameOps { def isFieldName = name endsWith LOCAL_SUFFIX def isInheritedName = name.length > 0 && name.head == '(' && name.startsWith(nme.INHERITED) def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0 - + def isScala2LocalSuffix = name.endsWith(" ") def isModuleVarName(name: Name): Boolean = name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX @@ -277,7 +277,7 @@ object NameOps { } def stripScala2LocalSuffix: TermName = - if (name.endsWith(" ")) name.init.asTermName else name + if (name.isScala2LocalSuffix) name.init.asTermName else name /** The name of an accessor for protected symbols. */ def protectedAccessorName: TermName = From a93d95517e2c2345e5e3ef4158bcf1d31a1342ba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:41:07 +0100 Subject: [PATCH 27/42] Accessor methods print as "method", not "val/var". --- src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- src/dotty/tools/dotc/typer/ErrorReporting.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index eace75433cfd..78ee32b98c3c 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -288,7 +288,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (flags is Mutable) "variable" else if (sym.isClassConstructor && sym.isPrimaryConstructor) "primary constructor" else if (sym.isClassConstructor) "constructor" - else if (sym.isSourceMethod) "method" + else if (sym.is(Method)) "method" else if (sym.isTerm) "value" else "" } diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index e96e04b1a449..8e8cf58f9b5c 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -35,7 +35,7 @@ object ErrorReporting { // See test pending/pos/boundspropagation.scala val treeSym = ctx.symOfContextTree(tree) if (treeSym.exists && treeSym.name == cycleSym.name && treeSym.owner == cycleSym.owner) { - val result = if (cycleSym.isSourceMethod) " result" else "" + val result = if (cycleSym is Method) " result" else "" d"overloaded or recursive $cycleSym needs$result type" } else errorMsg(msg, cx.outer) From 82635eb51d81b3d54e79123a726a63aa4528ba58 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:41:53 +0100 Subject: [PATCH 28/42] Small polishings in docs and code. --- src/dotty/tools/dotc/core/Denotations.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 82fd60fa0cef..ce11759ceb5c 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -202,7 +202,7 @@ object Denotations { def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol = info.member(name.toTypeName).requiredSymbol(_.isClass).asClass - /** The denotation that has a type matching `targetType` when seen + /** The alternative of this denotation that has a type matching `targetType` when seen * as a member of type `site`, `NoDenotation` if none exists. */ def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 6dce5581de8f..3953562b2655 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1843,7 +1843,7 @@ object Types { extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType => override val resultType = resultTypeExp(this) - assert(resultType != NoType) + assert(resultType.exists) def isJava = false def isImplicit = false From d8fa9ffd93c82ec7182d824d1d0b81168b6177c1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:42:42 +0100 Subject: [PATCH 29/42] More robost handling of isSetter/isGetter Now survives the case where a field is written x_=, dissumulating a setter. --- src/dotty/tools/dotc/core/SymDenotations.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 99b1c9f7788b..0d92a8e5f60d 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -425,10 +425,14 @@ object SymDenotations { final def isSourceMethod(implicit ctx: Context) = this is (Method, butNot = Accessor) /** Is this a setter? */ - final def isGetter(implicit ctx: Context) = (this is Accessor) && !originalName.isSetterName + final def isGetter(implicit ctx: Context) = + (this is Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix /** Is this a setter? */ - final def isSetter(implicit ctx: Context) = (this is Accessor) && originalName.isSetterName + final def isSetter(implicit ctx: Context) = + (this is Accessor) && + originalName.isSetterName && + info.firstParamTypes.nonEmpty // to avoid being fooled by var x_= : Unit = ... /** is this the constructor of a class? */ final def isClassConstructor = name == nme.CONSTRUCTOR @@ -892,8 +896,9 @@ object SymDenotations { override def valRef(implicit ctx: Context): TermRef = TermRef.withSigAndDenot(owner.thisType, name.asTermName, Signature.NotAMethod, this) - override def termRefWithSig(implicit ctx: Context): TermRef = - TermRef.withSigAndDenot(owner.thisType, name.asTermName, signature, this) + override def termRefWithSig(implicit ctx: Context): TermRef = // TODO generalize + if (ctx.erasedTypes) TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm) + else TermRef.withSigAndDenot(owner.thisType, name.asTermName, signature, this) def nonMemberTermRef(implicit ctx: Context): TermRef = TermRef.withFixedSym(owner.thisType, name.asTermName, symbol.asTerm) From 746a256cd76a36d7d484ea0b2d43aadeb1801ea1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:43:11 +0100 Subject: [PATCH 30/42] Adding test for Unit vars. --- tests/pos/blockescapes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pos/blockescapes.scala b/tests/pos/blockescapes.scala index 35d40974b1fe..68ce37ed3ee4 100644 --- a/tests/pos/blockescapes.scala +++ b/tests/pos/blockescapes.scala @@ -3,7 +3,7 @@ object blockescapes { { val x = 0; () } val x0 = { class Foo; new Foo } val x1 = {} - val x2 = { val z = 0 } + var x2 = { val z = 0 } val m1 = { val x = 2; x } trait T From 56cfa86fff87a7e4e506153f3789ca2a8852d22c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 09:45:30 +0100 Subject: [PATCH 31/42] New Mixin scheme. Split into two phases, ResolveSuper before Erasure and Mixin after. Likewise GettersSetters is split into Getters and Memoize. All tests pass, except two tests fail when compiled twice. Will investigate next why. --- src/dotty/tools/dotc/Compiler.scala | 8 +- src/dotty/tools/dotc/transform/Getters.scala | 67 +++++ src/dotty/tools/dotc/transform/Memoize.scala | 68 +++++ src/dotty/tools/dotc/transform/Mixin.scala | 257 +++++++----------- src/dotty/tools/dotc/transform/MixinOps.scala | 37 +++ .../tools/dotc/transform/ResolveSuper.scala | 95 +++++++ test/dotc/tests.scala | 4 +- 7 files changed, 365 insertions(+), 171 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/Getters.scala create mode 100644 src/dotty/tools/dotc/transform/Memoize.scala create mode 100644 src/dotty/tools/dotc/transform/MixinOps.scala create mode 100644 src/dotty/tools/dotc/transform/ResolveSuper.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 62759ba09f71..9da00f38a01d 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -50,10 +50,12 @@ class Compiler { new Splitter), List(new ElimByName, new InterceptedMethods, - new Literalize), - List(new Mixin), - List(new GettersSetters), + new Literalize, + new Getters), + List(new ResolveSuper), List(new Erasure), + List(new Mixin), + List(new Memoize), List(new CapturedVars, new Constructors), List(new LambdaLift, diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala new file mode 100644 index 000000000000..55b70a790733 --- /dev/null +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -0,0 +1,67 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Contexts.Context +import SymDenotations.SymDenotation +import Types._ +import Symbols._ +import SymUtils._ +import Constants._ +import TreeTransforms._ +import Flags._ +import Decorators._ + +/** Performs the following rewritings for fields of a class: + * + * val x: T = e + * --> def x: T = e + * var x: T = e + * --> def x: T = e + * + * val x: T + * --> def x: T + * + * var x: T + * --> def x: T + * + * Omitted from the rewritings are + * + * - private[this] fields in non-trait classes + * - fields generated for static modules (TODO: needed?) + * - parameters, static fields, and fields coming from Java + * + * Furthermore, assignements to mutable vars are replaced by setter calls + * + * p.x = e + * --> p.x_=(e) + */ +class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "getters" + override def treeTransformPhase = thisTransform.next + + override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { + def noGetterNeeded = + d.is(NoGetterNeeded) || + d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || + d.is(Module) && d.isStatic || + d.isSelfSym + if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) { + val maybeStable = if (d.isStable) Stable else EmptyFlags + d.copySymDenotation( + initFlags = d.flags | maybeStable | AccessorCreationFlags, + info = ExprType(d.info)) + } + else d + } + private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree + + override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs) else tree +} diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala new file mode 100644 index 000000000000..db9349bdb76f --- /dev/null +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -0,0 +1,68 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers._ +import Phases.Phase +import Contexts.Context +import SymDenotations.SymDenotation +import Types._ +import Symbols._ +import SymUtils._ +import Constants._ +import ast.Trees._ +import TreeTransforms._ +import NameOps._ +import Flags._ +import Decorators._ + +/** Provides the implementations of all getters and setters, introducing + * fields to hold the value accessed by them. + * TODO: Make LazyVals a part of this phase? + * + * def x(): T = e + * --> private val x: T = e + * def x(): T = x + * + * def x(): T = e + * --> private var x: T = e + * def x(): T = x + * + * def x_=(y: T): Unit = () + * --> def x_=(y: T): Unit = x = y + */ + class Memoize extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "memoize" + override def treeTransformPhase = thisTransform.next + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val sym = tree.symbol + if (sym.is(Accessor, butNot = NoFieldNeeded)) + if (sym.isGetter) { + val maybeMutable = if (sym is Stable) EmptyFlags else Mutable + println(i"add field for $sym") + val field = ctx.newSymbol( + owner = ctx.owner, + name = sym.name.asTermName.fieldName, + flags = Private | maybeMutable, + info = sym.info.resultType, + coord = tree.pos).enteredAfter(thisTransform) + var fieldInit = tree.rhs.changeOwner(sym, field) + val fieldDef = ValDef(field, fieldInit) + val getterDef = cpy.DefDef(tree)(rhs = ref(field)) + Thicket(fieldDef, getterDef) + } + else if (sym.isSetter) { + val Literal(Constant(())) = tree.rhs + assert(sym.field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") + val initializer = Assign(ref(sym.field), ref(tree.vparamss.head.head.symbol)) + cpy.DefDef(tree)(rhs = initializer) + } + else tree // curiously, some accessors from Scala2 have ' ' suffixes. They count as + // neither getters nor setters + else tree + } + private val NoFieldNeeded = Lazy | Deferred | ParamAccessor +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 8116c723a562..59bb8b910bf1 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -14,19 +14,19 @@ import DenotTransformers._ import StdNames._ import NameOps._ import ast.Trees._ -import util.Positions._ import collection.mutable +// todo: interface /** This phase performs the following transformations: * - * 1. (done in `traitDefs`) Map every concrete trait field + * 1. (done in `traitDefs`) Map every concrete trait getter * - * val x: T = expr + * def x(): T = expr * * to the pair of definitions: * - * val x: T - * protected def initial$x: T = { stats; expr } + * def x(): T + * protected def initial$x(): T = { stats; expr } * * where `stats` comprises all statements between either the start of the trait * or the previous field definition which are not definitions (i.e. are executed for @@ -45,33 +45,18 @@ import collection.mutable * For every trait M directly implemented by the class (see SymUtils.mixin), in * reverse linearization order, add the following definitions to C: * - * 3.1 (done in `traitInits`) For every concrete trait field ` val x: T` in M, + * 3.1 (done in `traitInits`) For every concrete trait getter ` def x(): T` in M, * in order of textual occurrence: * - * val x: T = super[M].initial$x + * def x(): T = super[M].initial$x() * - * 3.2 (done in `constrCall`) The call: + * 3.2 (done in `superCallOpt`) The call: * * super[M]. * * 3.3 (done in `setters`) For every concrete setter ` def x_=(y: T)` in M: * * def x_=(y: T) = () - * - * 3.4 (done in `superAccessors`) For every superAccessor - * ` def super$f[Ts](ps1)...(psN): U` in M: - * - * def super$f[Ts](ps1)...(psN): U = super[S].f[Ts](ps1)...(psN) - * - * where `S` is the superclass of `M` in the linearization of `C`. - * - * 3.5 (done in `methodOverrides`) For every method - * ` def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: - * - * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) - * - * A method in M needs to be disambiguated if it is concrete, not overridden in C, - * and if it overrides another concrete method. */ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ @@ -81,166 +66,106 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => override def treeTransformPhase = thisTransform.next override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = - if (sym is Trait) { - lazy val newDecls = sym.decls.cloneScope - var hasInit = false - for (d <- sym.decls) - if (d.isTerm && !d.is(MethodOrDeferred)) { - hasInit = true - val initializer = ctx.newSymbol( - sym.symbol, - InitializerName(d.asTerm.name), - Protected | Synthetic | Method, - ExprType(d.info), - coord = d.coord) - newDecls.enter(initializer) - } - if (hasInit) { - val cinfo = sym.asClass.classInfo - sym.copySymDenotation(info = cinfo.derivedClassInfo(decls = newDecls)) - } - else sym - } - else if ((sym.isField || sym.isSetter) && sym.owner.is(Trait) && !sym.is(Deferred)) + if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) sym.copySymDenotation(initFlags = sym.flags | Deferred) else sym - private val MethodOrDeferred = Method | Deferred - private val PrivateOrDeferred = Private | Deferred - - private def traitDefs(cls: ClassSymbol, stats: List[Tree])(implicit ctx: Context): List[Tree] = { - val initBuf = new mutable.ListBuffer[Tree] - stats flatMap { - case stat: ValDef if !stat.rhs.isEmpty => - val vsym = stat.symbol - val isym = vsym.initializer - val rhs = Block( - initBuf.toList, - stat.rhs.changeOwner(vsym, isym).ensureConforms(isym.info.widen)) - initBuf.clear() - List( - cpy.ValDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), - DefDef(stat.symbol.initializer, rhs)) - case stat: DefDef if stat.symbol.isSetter => - List(cpy.DefDef(stat)( - mods = stat.mods | Deferred, - rhs = EmptyTree)) - case stat: DefTree => - List(stat) - case stat => - initBuf += stat - Nil - } + private def initializer(sym: Symbol)(implicit ctx: Context): TermSymbol = { + val initName = InitializerName(sym.name.asTermName) + sym.owner.info.decl(initName).symbol + .orElse( + ctx.newSymbol( + sym.owner, + initName, + Protected | Synthetic | Method, + sym.info, + coord = sym.symbol.coord).enteredAfter(thisTransform)) + .asTerm } - /** Returns the symbol that is accessed by a super-accessor in a mixin composition. - * - * @param base The class in which everything is mixed together - * @param member The symbol statically referred to by the superaccessor in the trait - */ - private def rebindSuper(base: Symbol, acc: Symbol)(implicit ctx: Context): Symbol = { - var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail - var sym: Symbol = NoSymbol - val SuperAccessorName(memberName) = acc.name - ctx.debuglog("starting rebindsuper " + base + " " + memberName + ":" + acc.info + - " " + acc.owner + " " + base.info.baseClasses + "/" + bcs) - while (!bcs.isEmpty && sym == NoSymbol) { - val other = bcs.head.info.nonPrivateDecl(memberName) - if (ctx.settings.debug.value) - ctx.debuglog("rebindsuper " + bcs.head + " " + other + " " + other.info + - " " + other.symbol.is(Deferred)) - sym = other.matchingDenotation(base.thisType, acc.info).symbol - bcs = bcs.tail + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = impl.symbol.owner.asClass + val ops = new MixinOps(cls, thisTransform) + import ops._ + + def traitDefs(stats: List[Tree]): List[Tree] = { + val initBuf = new mutable.ListBuffer[Tree] + stats flatMap { + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty => + val vsym = stat.symbol + val isym = initializer(vsym) + val rhs = Block( + initBuf.toList.map(_.changeOwner(impl.symbol, isym)), + stat.rhs.changeOwner(vsym, isym)) + initBuf.clear() + List( + cpy.DefDef(stat)(mods = stat.mods | Deferred, rhs = EmptyTree), + DefDef(isym, rhs)) + case stat: DefDef if stat.symbol.isSetter => + List(cpy.DefDef(stat)( + mods = stat.mods | Deferred, + rhs = EmptyTree)) + case stat: DefTree => + List(stat) + case stat => + initBuf += stat + Nil + } } - sym - } - - private def superAccessors(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = - for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) - yield polyDefDef(implementation(cls, superAcc.asTerm), forwarder(cls, rebindSuper(cls, superAcc))) - - private def traitInits(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = - for (field <- mixin.decls.filter(fld => fld.isField && !wasDeferred(fld)).toList) - yield ValDef(implementation(cls, field.asTerm), superRef(cls, field.initializer, cls.pos)) - private def setters(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = - for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) - yield DefDef(implementation(cls, setter.asTerm), unitLiteral.withPos(cls.pos)) - - private def implementation(cls: ClassSymbol, member: TermSymbol)(implicit ctx: Context): TermSymbol = - member.copy( - owner = cls, - name = member.name.stripScala2LocalSuffix, - flags = member.flags &~ Deferred).enteredAfter(thisTransform).asTerm - - private def methodOverrides(cls: ClassSymbol, mixin: ClassSymbol)(implicit ctx: Context): List[Tree] = { - def isOverridden(meth: Symbol) = meth.overridingSymbol(cls).is(Method, butNot = Deferred) - def needsDisambiguation(meth: Symbol): Boolean = - meth.is(Method, butNot = PrivateOrDeferred) && - !isOverridden(meth) && - !meth.allOverriddenSymbols.forall(_ is Deferred) - for (meth <- mixin.decls.toList if needsDisambiguation(meth)) - yield polyDefDef(implementation(cls, meth.asTerm), forwarder(cls, meth)) - } - - private def wasDeferred(sym: Symbol)(implicit ctx: Context) = - ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } + def transformSuper(tree: Tree): Tree = { + val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree + superRef(tree.symbol, tree.pos).appliedToArgs(args) + } - private def superRef(cls: ClassSymbol, target: Symbol, pos: Position)(implicit ctx: Context) = { - val inTrait = target.owner is Trait - Super( - qual = This(cls), - mix = if (inTrait) target.owner.name.asTypeName else tpnme.EMPTY, - inConstrCall = target.isConstructor && !target.owner.is(Trait), - mixinClass = if (inTrait) target.owner else NoSymbol - ).select(target) - } + val superCalls = ( + for (p <- impl.parents if p.symbol.isConstructor) + yield p.symbol.owner -> transformSuper(p) + ).toMap - private def forwarder(cls: ClassSymbol, target: Symbol)(implicit ctx: Context) = - (targs: List[Type]) => (vrefss: List[List[Tree]]) => - superRef(cls, target, cls.pos).appliedToTypes(targs).appliedToArgss(vrefss) + def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { + case Some(call) => + if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil + case None => + if (baseCls.is(Interface) || defn.PhantomClasses.contains(baseCls)) Nil + else { + //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") + superRef(baseCls.primaryConstructor).appliedToNone :: Nil +/* constr.tpe.widen match { + case tpe: PolyType => + val targs = cls.thisType.baseTypeWithArgs(baseCls).argTypes + constr = constr.appliedToTypes(targs) + case _ => + } + constr.ensureApplied :: Nil +*/ + } + } - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { - val cls = tree.symbol.owner.asClass - val stats = tree.body - val superCls = cls.classInfo.parents.head.symbol - val mixins = cls.baseClasses.tail.takeWhile(_ ne superCls) + def wasDeferred(sym: Symbol) = + ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } - // If parent is a constructor call, pull out the call into a separate - // supercall constructor, which gets added to `superCalls`, and keep - // only the type. - val superCalls = new mutable.HashMap[Symbol, Tree] - def normalizeParent(tree: Tree) = tree match { - case superApp @ Apply(superSel @ Select(New(superType), nme.CONSTRUCTOR), superArgs) => - val constr = superSel.symbol - superCalls(constr.owner) = superRef(cls, constr, superSel.pos).appliedToArgs(superArgs) - superType - case tree: TypeTree => tree - } - val parentTypeTrees = tree.parents.map(normalizeParent) + def traitInits(mixin: ClassSymbol): List[Tree] = + for (getter <- mixin.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) + yield { + println(i"impl for getter $getter!") + DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) + } - def supCalls(baseCls: Symbol): List[Tree] = superCalls.remove(baseCls) match { - case Some(call) => call :: Nil - case None => - if (baseCls is Interface) Nil - else superRef(cls, baseCls.primaryConstructor, cls.pos).appliedToNone :: Nil - } + def setters(mixin: ClassSymbol): List[Tree] = + for (setter <- mixin.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) + yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos)) - cpy.Template(tree)( - parents = parentTypeTrees, + cpy.Template(impl)( + parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), body = - if (cls is Trait) traitDefs(cls, stats) + if (cls is Trait) traitDefs(impl.body) else { val mixInits = mixins.flatMap { mixin => - assert(mixin is Trait) - traitInits(cls, mixin) ::: - supCalls(mixin) ::: - setters(cls, mixin) ::: - superAccessors(cls, mixin) ::: - methodOverrides(cls, mixin) + traitInits(mixin) ::: superCallOpt(mixin) ::: setters(mixin) } - supCalls(superCls) ::: mixInits ::: stats + superCallOpt(superCls) ::: mixInits ::: impl.body }) } -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala new file mode 100644 index 000000000000..de15b045ff61 --- /dev/null +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -0,0 +1,37 @@ +package dotty.tools.dotc +package transform + +import core._ +import Symbols._, Types._, Contexts._, SymDenotations._, DenotTransformers._, Flags._ +import util.Positions._ +import StdNames._, NameOps._ + +class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: Context) { + import ast.tpd._ + + val superCls: Symbol = cls.classInfo.parents.head.symbol + val mixins: List[ClassSymbol] = + if (cls is Trait) Nil + else cls.baseClasses.tail.takeWhile(_ ne superCls).reverse + + def implementation(member: TermSymbol): TermSymbol = + member.copy( + owner = cls, + name = member.name.stripScala2LocalSuffix, + flags = member.flags &~ Deferred &~ Module, + info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm + + def superRef(target: Symbol, pos: Position = cls.pos): Tree = { + val sup = if (target.isConstructor && !target.owner.is(Trait)) + Super(This(cls), tpnme.EMPTY, true) + else + Super(This(cls), target.owner.name.asTypeName, false, target.owner) + //println(i"super ref $target on $sup") + ast.untpd.Select(sup.withPos(pos), target.name) + .withType(NamedType.withFixedSym(sup.tpe, target)) + //sup.select(target) + } + + def forwarder(target: Symbol) = (targs: List[Type]) => (vrefss: List[List[Tree]]) => + superRef(target).appliedToTypes(targs).appliedToArgss(vrefss) +} diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala new file mode 100644 index 000000000000..1fd8b122f4a5 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -0,0 +1,95 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import ast.Trees._ +import util.Positions._ +import Names._ +import collection.mutable + +/** This phase adds super accessors and method overrides where + * linearization differs from Java's rule for default methods in interfaces. + * In particular: + * + * For every trait M directly implemented by the class (see SymUtils.mixin), in + * reverse linearization order, add the following definitions to C: + * + * 3.1 (done in `superAccessors`) For every superAccessor + * ` def super$f[Ts](ps1)...(psN): U` in M: + * + * def super$f[Ts](ps1)...(psN): U = super[S].f[Ts](ps1)...(psN) + * + * where `S` is the superclass of `M` in the linearization of `C`. + * + * 3.2 (done in `methodOverrides`) For every method + * ` def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: + * + * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) + * + * A method in M needs to be disambiguated if it is concrete, not overridden in C, + * and if it overrides another concrete method. + */ +class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName: String = "resolveSuper" + + override def treeTransformPhase = thisTransform.next + + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = impl.symbol.owner.asClass + val ops = new MixinOps(cls, thisTransform) + import ops._ + + /** Returns the symbol that is accessed by a super-accessor in a mixin composition. + * + * @param base The class in which everything is mixed together + * @param member The symbol statically referred to by the superaccessor in the trait + */ + def rebindSuper(base: Symbol, acc: Symbol): Symbol = { + var bcs = cls.info.baseClasses.dropWhile(acc.owner != _).tail + var sym: Symbol = NoSymbol + val SuperAccessorName(memberName) = acc.name: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type + println(i"starting rebindsuper from $cls of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") + while (bcs.nonEmpty && sym == NoSymbol) { + val other = bcs.head.info.nonPrivateDecl(memberName) + //if (ctx.settings.debug.value) + println(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}") + sym = other.matchingDenotation(cls.thisType, cls.thisType.memberInfo(acc)).symbol + bcs = bcs.tail + } + assert(sym.exists) + sym + } + + def superAccessors(mixin: ClassSymbol): List[Tree] = + for (superAcc <- mixin.decls.filter(_ is SuperAccessor).toList) + yield polyDefDef(implementation(superAcc.asTerm), forwarder(rebindSuper(cls, superAcc))) + + def methodOverrides(mixin: ClassSymbol): List[Tree] = { + def isOverridden(meth: Symbol) = meth.overridingSymbol(cls).is(Method, butNot = Deferred) + def needsDisambiguation(meth: Symbol): Boolean = + meth.is(Method, butNot = PrivateOrDeferred) && + !isOverridden(meth) && + !meth.allOverriddenSymbols.forall(_ is Deferred) + for (meth <- mixin.decls.toList if needsDisambiguation(meth)) + yield polyDefDef(implementation(meth.asTerm), forwarder(meth)) + } + + val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) + + cpy.Template(impl)(body = overrides ::: impl.body) + } + private val PrivateOrDeferred = Private | Deferred +} diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 2b0d7eb0837c..39c6658d0644 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -104,12 +104,12 @@ class tests extends CompilerTest { @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @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_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_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" :: Nil)(allowDeepSubtypes) From dbd9393c80b1d3d64adfc6173b6127cad89104ea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:02:54 +0100 Subject: [PATCH 32/42] Retract of optimization in computeDenot Needs a weakening of an assertion in assertErased --- src/dotty/tools/dotc/core/Types.scala | 11 +++++++++-- src/dotty/tools/dotc/transform/Erasure.scala | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 3953562b2655..5d95fae7600a 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1175,8 +1175,15 @@ object Types { case d => if (d.validFor.runId != ctx.period.runId) loadDenot - else if (ctx.erasedTypes && lastSymbol != null) - denotOfSym(lastSymbol) // avoid keeping non-sym denotations after erasure; they violate the assertErased contract + // The following branch was used to avoid an assertErased error. + // It's idea was to void keeping non-sym denotations after erasure + // since they violate the assertErased contract. But the problem is + // that when seen again in an earlier phase the denotation is + // still seen as a SymDenotation, whereas it should be a SingleDenotation. + // That's why the branch is disabled. + // + // else if (ctx.erasedTypes && lastSymbol != null) + // denotOfSym(lastSymbol) else d.current } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index b51a2360e28e..0a34b9e7ca16 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -94,7 +94,8 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => if (ctx.mode.isExpr) tree.tpe match { case ref: TermRef => - assert(ref.denot.isInstanceOf[SymDenotation], + assert(ref.denot.isInstanceOf[SymDenotation] || + ref.denot.isInstanceOf[UniqueRefDenotation], i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree") case _ => } From dc16bad57f6c46f95f8ecddf1ee8a15a5da89c38 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:05:54 +0100 Subject: [PATCH 33/42] Fixed handling of ThisTypes in TypeErasure. Need to be treated like TermRefs. --- src/dotty/tools/dotc/TypeErasure.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 448dd3f57a6a..4a492560f581 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -108,6 +108,8 @@ object TypeErasure { case tp: TermRef => assert(tp.symbol.exists, tp) TermRef(erasedRef(tp.prefix), tp.symbol.asTerm) + case tp: ThisType => + tp case tp => erasure(tp) } @@ -271,7 +273,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild case tp: TermRef => this(tp.widen) case ThisType(_) => - tp + this(tp.widen) case SuperType(thistpe, supertpe) => SuperType(this(thistpe), this(supertpe)) case ExprType(rt) => From bf45e65a8a3a13bb6f43c51291e9e31ed8e8277a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:07:47 +0100 Subject: [PATCH 34/42] Better error message for adaptations of implicit methods Ig an implicit method has a result type which does not match the expected type, the new case fires. We avoid to diagnose missing arguments, because that's really confusing for an implicit method. --- src/dotty/tools/dotc/typer/Typer.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 59fda174df1e..a5396d445fa5 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1245,6 +1245,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(etaExpand(tree, wtp, arity), pt) else if (wtp.paramTypes.isEmpty) adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree) + else if (wtp.isImplicit) + err.typeMismatch(tree, pt) else errorTree(tree, d"""missing arguments for $methodStr From da5b42739f189756864db37e698de66588ea4be9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 12:10:08 +0100 Subject: [PATCH 35/42] Reinstantiated full tests. The fix to Types in 9bb35512 (Retract of optimization in computeDenot) allowed the two tests to compile "twice" again. --- test/dotc/tests.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 39c6658d0644..2b0d7eb0837c 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -104,12 +104,12 @@ class tests extends CompilerTest { @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @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_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_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" :: Nil)(allowDeepSubtypes) From 8c22e7639d06a5b6c36585c577ca21cead443016 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:42:55 +0100 Subject: [PATCH 36/42] Fixed bug in treating Thickets in TreeTransform --- src/dotty/tools/dotc/transform/TreeTransform.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 2037bede8553..850563a484da 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -905,7 +905,7 @@ object TreeTransforms { case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur)) case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur)) case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur)) - case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) + case Thicket(trees) => tree case tree => goOther(tree, info.nx.nxTransOther(cur)) } @@ -1164,7 +1164,8 @@ object TreeTransforms { val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx) goPackageDef(cpy.PackageDef(tree)(pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) } - case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) + case Thicket(trees) => + cpy.Thicket(tree)(transformTrees(trees, info, cur)) case tree => implicit val originalInfo: TransformerInfo = info goOther(tree, info.nx.nxTransOther(cur)) From 34b7d359f0e983cca75e75ed4bec31517fa312ef Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:44:30 +0100 Subject: [PATCH 37/42] New functionality: changeOwnerAfter Changes owners after a phase without copying the tree. This should be more suitable for the changeOwner operations used in the tree transforms so far, which are linear, i.e. no tree duplication is needed. --- src/dotty/tools/dotc/ast/tpd.scala | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 6aa715c8ed4e..5aebbd6fe005 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -6,7 +6,7 @@ import transform.SymUtils._ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._ -import Denotations._, Decorators._ +import Denotations._, Decorators._, DenotTransformers._ import config.Printers._ import typer.Mode import collection.mutable @@ -537,6 +537,28 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { loop(from, Nil, to :: Nil) } + /** After phase `trans`, set the owner of every definition in this tree that was formerly + * owner by `from` to `to`. + */ + def changeOwnerAfter(from: Symbol, to: Symbol, trans: DenotTransformer)(implicit ctx: Context): ThisTree = { + assert(ctx.phase == trans.next) + val traverser = new TreeTraverser { + def traverse(tree: Tree) = tree match { + case tree: DefTree => + val sym = tree.symbol + if (sym.denot(ctx.withPhase(trans)).owner == from) { + println(i"change owner $from -> $to of $sym") + sym.copySymDenotation(owner = to).installAfter(trans) + } + if (sym.isWeakOwner) traverseChildren(tree) + case _ => + traverseChildren(tree) + } + } + traverser.traverse(tree) + tree + } + def select(name: Name)(implicit ctx: Context): Select = Select(tree, name) From cab28b24d2d4f72edfe54abcacb383b82392af17 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:45:17 +0100 Subject: [PATCH 38/42] Merge memoize with miniphases following it into one group. --- src/dotty/tools/dotc/Compiler.scala | 8 ++-- src/dotty/tools/dotc/transform/Memoize.scala | 40 +++++++++++++------- src/dotty/tools/dotc/transform/Mixin.scala | 1 - test/dotc/tests.scala | 2 +- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 9da00f38a01d..ae5d5c1003e7 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -51,12 +51,12 @@ class Compiler { List(new ElimByName, new InterceptedMethods, new Literalize, - new Getters), - List(new ResolveSuper), + new Getters, + new ResolveSuper), List(new Erasure), List(new Mixin), - List(new Memoize), - List(new CapturedVars, + List(new Memoize, + new CapturedVars, new Constructors), List(new LambdaLift, new Flatten, diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index db9349bdb76f..6b14d77146a3 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -37,28 +37,40 @@ import Decorators._ override def phaseName = "memoize" override def treeTransformPhase = thisTransform.next + override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = { + val sym = tree.symbol + if (sym.isGetter && !sym.is(NoFieldNeeded)) { + // allocate field early so that initializer has the right owner for subsequeny phases in + // the group. + val maybeMutable = if (sym is Stable) EmptyFlags else Mutable + val field = ctx.newSymbol( + owner = ctx.owner, + name = sym.name.asTermName.fieldName, + flags = Private | maybeMutable, + info = sym.info.resultType, + coord = tree.pos).enteredAfter(thisTransform) + tree.rhs.changeOwnerAfter(sym, field, thisTransform) + } + this + } + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { val sym = tree.symbol + def field = { + val field = sym.field.asTerm + assert(field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") + field + } if (sym.is(Accessor, butNot = NoFieldNeeded)) if (sym.isGetter) { - val maybeMutable = if (sym is Stable) EmptyFlags else Mutable - println(i"add field for $sym") - val field = ctx.newSymbol( - owner = ctx.owner, - name = sym.name.asTermName.fieldName, - flags = Private | maybeMutable, - info = sym.info.resultType, - coord = tree.pos).enteredAfter(thisTransform) - var fieldInit = tree.rhs.changeOwner(sym, field) - val fieldDef = ValDef(field, fieldInit) - val getterDef = cpy.DefDef(tree)(rhs = ref(field)) + val fieldDef = transformFollowing(ValDef(field, tree.rhs)) + val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))) Thicket(fieldDef, getterDef) } else if (sym.isSetter) { val Literal(Constant(())) = tree.rhs - assert(sym.field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %") - val initializer = Assign(ref(sym.field), ref(tree.vparamss.head.head.symbol)) - cpy.DefDef(tree)(rhs = initializer) + val initializer = Assign(ref(field), ref(tree.vparamss.head.head.symbol)) + cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)) } else tree // curiously, some accessors from Scala2 have ' ' suffixes. They count as // neither getters nor setters diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 59bb8b910bf1..668aca215ad0 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -149,7 +149,6 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitInits(mixin: ClassSymbol): List[Tree] = for (getter <- mixin.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { - println(i"impl for getter $getter!") DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone) } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 2b0d7eb0837c..5cce45e215df 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,7 +15,7 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-Yno-deep-subtypes", - "-Ycheck:patternMatcher,mixin,gettersSetters,restoreScopes" + "-Ycheck:resolveSuper,mixin,restoreScopes" ) val twice = List("#runs", "2", "-YnoDoubleBindings") From 033c6861e06a1db08a62a49f29b950a01ca248ff Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Nov 2014 18:48:49 +0100 Subject: [PATCH 39/42] Merge mixin with the group of miniphases following it. --- src/dotty/tools/dotc/Compiler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index ae5d5c1003e7..819b60d48332 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -54,8 +54,8 @@ class Compiler { new Getters, new ResolveSuper), List(new Erasure), - List(new Mixin), - List(new Memoize, + List(new Mixin, + new Memoize, new CapturedVars, new Constructors), List(new LambdaLift, From dc276eb73cb4fb34109ce88938bf9585a07448c0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Nov 2014 10:36:37 +0100 Subject: [PATCH 40/42] More docs and removing print statements --- src/dotty/tools/dotc/Compiler.scala | 3 +-- src/dotty/tools/dotc/ast/tpd.scala | 4 +--- src/dotty/tools/dotc/transform/Getters.scala | 2 ++ src/dotty/tools/dotc/transform/Memoize.scala | 6 ++++++ src/dotty/tools/dotc/transform/Mixin.scala | 10 ++++++++-- src/dotty/tools/dotc/transform/ResolveSuper.scala | 9 ++++++--- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 819b60d48332..004a3868c8af 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -46,7 +46,6 @@ class Compiler { new TailRec), List(new PatternMatcher, new ExplicitOuter, - // new LazyValTranformContext().transformer, // disabled, awaiting fixes new Splitter), List(new ElimByName, new InterceptedMethods, @@ -55,7 +54,7 @@ class Compiler { new ResolveSuper), List(new Erasure), List(new Mixin, - new Memoize, + new Memoize, // TODO: Make LazyVals a part of this phase new CapturedVars, new Constructors), List(new LambdaLift, diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 5aebbd6fe005..735b218e3d56 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -546,10 +546,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def traverse(tree: Tree) = tree match { case tree: DefTree => val sym = tree.symbol - if (sym.denot(ctx.withPhase(trans)).owner == from) { - println(i"change owner $from -> $to of $sym") + if (sym.denot(ctx.withPhase(trans)).owner == from) sym.copySymDenotation(owner = to).installAfter(trans) - } if (sym.isWeakOwner) traverseChildren(tree) case _ => traverseChildren(tree) diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala index 55b70a790733..4ea9d2c6b6a4 100644 --- a/src/dotty/tools/dotc/transform/Getters.scala +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -36,6 +36,8 @@ import Decorators._ * * p.x = e * --> p.x_=(e) + * + * No fields are generated yet. This is done later in phase Memoize. */ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index 6b14d77146a3..ef70b9ecf34a 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -37,6 +37,12 @@ import Decorators._ override def phaseName = "memoize" override def treeTransformPhase = thisTransform.next + /** Should to run after mixin so that fields get generated in the + * class that contains the concrete getter rather than the trait + * that defines it. + */ + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin]) + override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = { val sym = tree.symbol if (sym.isGetter && !sym.is(NoFieldNeeded)) { diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 668aca215ad0..1d342404a272 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -13,10 +13,10 @@ import Decorators._ import DenotTransformers._ import StdNames._ import NameOps._ +import Phases._ import ast.Trees._ import collection.mutable -// todo: interface /** This phase performs the following transformations: * * 1. (done in `traitDefs`) Map every concrete trait getter @@ -57,14 +57,20 @@ import collection.mutable * 3.3 (done in `setters`) For every concrete setter ` def x_=(y: T)` in M: * * def x_=(y: T) = () + * + * Conceptually, this is the second half of the previous mixin phase. It needs to run + * after erasure because it copies references to possibly private inner classes and objects + * into enclosing classes where they are not visible. This can only be done if all references + * are symbolic. */ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ override def phaseName: String = "mixin" - override def treeTransformPhase = thisTransform.next + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) sym.copySymDenotation(initFlags = sym.flags | Deferred) diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala index 1fd8b122f4a5..23ff45a7c0b1 100644 --- a/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -39,6 +39,9 @@ import collection.mutable * * A method in M needs to be disambiguated if it is concrete, not overridden in C, * and if it overrides another concrete method. + * + * This is the first part of what was the mixin phase. It is complemented by + * Mixin, which runs after erasure. */ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => import ast.tpd._ @@ -61,11 +64,11 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th var bcs = cls.info.baseClasses.dropWhile(acc.owner != _).tail var sym: Symbol = NoSymbol val SuperAccessorName(memberName) = acc.name: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type - println(i"starting rebindsuper from $cls of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") + ctx.debuglog(i"starting rebindsuper from $cls of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") while (bcs.nonEmpty && sym == NoSymbol) { val other = bcs.head.info.nonPrivateDecl(memberName) - //if (ctx.settings.debug.value) - println(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}") + if (ctx.settings.debug.value) + ctx.log(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}") sym = other.matchingDenotation(cls.thisType, cls.thisType.memberInfo(acc)).symbol bcs = bcs.tail } From 2e8ca845be305a8eb03301fbc5cd723c5e5fcb3a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Nov 2014 13:16:42 +0100 Subject: [PATCH 41/42] Remove unused class It functionality is now all implemented in ExplicitOuter. --- .../tools/dotc/transform/AttachOuter.scala | 65 ------------------- 1 file changed, 65 deletions(-) delete mode 100644 src/dotty/tools/dotc/transform/AttachOuter.scala diff --git a/src/dotty/tools/dotc/transform/AttachOuter.scala b/src/dotty/tools/dotc/transform/AttachOuter.scala deleted file mode 100644 index 9d2b0574da61..000000000000 --- a/src/dotty/tools/dotc/transform/AttachOuter.scala +++ /dev/null @@ -1,65 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Symbols._ -import core.Contexts._ -import core.Types._ -import core.Flags._ -import core.Decorators._ -import core.StdNames.nme -import ast.Trees._ -import util.Attachment - -/** This phase decorates News and parent constructors of non-static inner classes - * with an attachment indicating the outer reference as a tree. This is necessary because - * outer prefixes are erased, and explicit outer runs only after erasure. - */ -class AttachOuter extends MiniPhaseTransform { - import ast.tpd._ - - val Outer = new Attachment.Key[Tree] - - override def phaseName: String = "attachOuter" - - private def outerPrefix(tpe: Type)(implicit ctx: Context): Type = tpe match { - case tpe: TypeRef => - tpe.symbol match { - case cls: ClassSymbol => - if (cls.owner.isStaticOwner || cls.is(Interface)) NoPrefix - else if (tpe.prefix eq NoPrefix) cls.owner.enclosingClass.thisType - else tpe.prefix - case _ => - outerPrefix(tpe.underlying) - } - case tpe: TypeProxy => - outerPrefix(tpe.underlying) - } - - override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo): Tree = { - val pre = outerPrefix(tree.tpt.tpe) - pre match { - case pre: SingletonType => - tree.putAttachment(Outer, singleton(pre)) match { - case Some(outer) => assert(outer.tpe =:= pre) - case none => - } - case NoPrefix => - } - tree - } - - override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { - def transformParent(tree: Tree): Tree = tree match { - case tree: TypeTree if outerPrefix(tree.tpe) != NoPrefix => - val constr = New(tree.tpe, Nil).withPos(tree.pos) - val Select(nu: New, _) = methPart(constr) - transformNew(nu) - constr - case _ => - tree - } - cpy.Template(tree)(parents = tree.parents mapconserve transformParent) - } -} \ No newline at end of file From c132ceafdd6fe4d223b67d2fe7ac732f5bbb3063 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Nov 2014 13:21:06 +0100 Subject: [PATCH 42/42] Clean up code relating to interfaces. Previous implementation was confused about the meaning of interface (with default methods or without?). Now instead of Interface/JavaInterface we have PureInterface - all members are abstract methods NoInits - all members are methods --- src/dotty/tools/dotc/ast/TreeInfo.scala | 15 +++++++++------ src/dotty/tools/dotc/core/Definitions.scala | 6 +++--- src/dotty/tools/dotc/core/Flags.scala | 18 ++++++++++++------ .../core/pickling/ClassfileConstants.scala | 2 +- .../dotc/core/pickling/ClassfileParser.scala | 5 +++-- .../dotc/core/pickling/PickleBuffer.scala | 2 +- .../tools/dotc/printing/PlainPrinter.scala | 2 +- .../tools/dotc/transform/ExplicitOuter.scala | 10 +++++----- src/dotty/tools/dotc/transform/Mixin.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 7 +++++++ 10 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 51e1ff16ffb7..8163c8bcc159 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -26,15 +26,18 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** Is tree legal as a member definition of an interface? */ - def isInterfaceMember(tree: Tree): Boolean = unsplice(tree) match { - case EmptyTree => true - case Import(_, _) => true - case TypeDef(_, _, _) => true - case DefDef(mods, _, _, _, _, __) => mods.flags is Deferred - case ValDef(mods, _, _, _) => mods is Deferred + def isPureInterfaceMember(tree: Tree): Boolean = unsplice(tree) match { + case EmptyTree | Import(_, _) | TypeDef(_, _, _) => true + case DefDef(_, _, _, _, _, rhs) => rhs.isEmpty + case ValDef(mods, _, _, rhs) => rhs.isEmpty case _ => false } + /** Is tree legal as a member definition of a no-init trait? + */ + def isNoInitMember(tree: Tree): Boolean = + isPureInterfaceMember(tree) || unsplice(tree).isInstanceOf[DefDef] + def isOpAssign(tree: Tree) = unsplice(tree) match { case Apply(fn, _ :: Nil) => unsplice(fn) match { diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index b1c2baff6cd7..d78e09418115 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -191,7 +191,7 @@ class Definitions { lazy val ScalaStaticsClass = ScalaStaticsModule.moduleClass.asClass def staticsMethod(name: PreName) = ctx.requiredMethod(ScalaStaticsClass, name) - + lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef") lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil") lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms) @@ -201,7 +201,7 @@ class Definitions { // needed as a synthetic class because Scala 2.x refers to it in classfiles // but does not define it as an explicit class. newCompleteClassSymbol( - ScalaPackageClass, tpnme.Singleton, Trait | Interface | Final, + ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final, List(AnyClass.typeRef), EmptyScope) lazy val SeqClass: ClassSymbol = ctx.requiredClass("scala.collection.Seq") lazy val Seq_apply = ctx.requiredMethod(SeqClass, nme.apply) @@ -506,7 +506,7 @@ class Definitions { val cls = newClassSymbol( ScalaPackageClass, traitName, - Trait | Interface | Synthetic, + PureInterfaceCreationFlags | Synthetic, completer) myLambdaTraits += cls cls diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 804f6af1a3f0..db969767b8c4 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -287,7 +287,7 @@ object Flags { /** A trait that has only abstract methods as members * (and therefore can be represented by a Java interface */ - final val Interface = typeFlag(22, "interface") + final val PureInterface = typeFlag(22, "interface") /** Labeled with of abstract & override */ final val AbsOverride = termFlag(22, "abstract override") @@ -335,6 +335,9 @@ object Flags { final val JavaStaticTerm = JavaStatic.toTermFlags final val JavaStaticType = JavaStatic.toTypeFlags + /** Trait is not an interface, but does not have fields or intialization code */ + final val NoInits = typeFlag(32, "") + /** Variable is accessed from nested function. */ final val Captured = termFlag(32, "") @@ -353,9 +356,6 @@ object Flags { /** Symbol is a Java-style varargs method */ final val JavaVarargs = termFlag(37, "") - /** Symbol is a Java default method */ - final val DefaultMethod = termFlag(38, "") - // Flags following this one are not pickled /** Symbol always defines a fresh named type */ @@ -464,6 +464,9 @@ object Flags { /** Accessors always have these flags set */ final val AccessorCreationFlags = Method | Accessor + /** Pure interfaces always have these flags */ + final val PureInterfaceCreationFlags = Trait | NoInits | PureInterface + /** The flags of the self symbol */ final val SelfSymFlags = Private | Local | Deferred @@ -539,8 +542,11 @@ object Flags { /** Is a default parameter in Scala 2*/ final val DefaultParameter = allOf(Param, DefaultParameterized) - /** A Java interface */ - final val JavaInterface = allOf(JavaDefined, Trait) + /** A trait that does not need to be initialized */ + final val NoInitsTrait = allOf(Trait, NoInits) + + /** A Java interface, potentially with default methods */ + final val JavaTrait = allOf(JavaDefined, Trait, NoInits) /** A Java companion object */ final val JavaModule = allOf(JavaDefined, Module) diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala index c35b9ca474fd..158f6b40973c 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala @@ -345,7 +345,7 @@ object ClassfileConstants { case JAVA_ACC_SYNTHETIC => Synthetic case JAVA_ACC_STATIC => JavaStatic case JAVA_ACC_ABSTRACT => if (isAnnotation) EmptyFlags else if (isClass) Abstract else Deferred - case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else JavaInterface + case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else PureInterfaceCreationFlags | JavaDefined case _ => EmptyFlags } diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 67f8255025f1..f2a5e41719e2 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -503,8 +503,9 @@ class ClassfileParser( parseExceptions(attrLen) case tpnme.CodeATTR => - if (sym.owner is Flags.Interface) { - sym.setFlag(Flags.DefaultMethod) + if (sym.owner is Flags.JavaTrait) { + sym.resetFlag(Flags.Deferred) + sym.owner.resetFlag(Flags.PureInterface) ctx.log(s"$sym in ${sym.owner} is a java8+ default method.") } in.skip(attrLen) diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala index d2a05bf3a4dc..9f8d4fc2d141 100644 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -220,7 +220,7 @@ object PickleBuffer { DEFERRED_PKL -> Deferred, FINAL_PKL -> Final, METHOD_PKL -> Method, - INTERFACE_PKL -> Interface, + INTERFACE_PKL -> PureInterface, MODULE_PKL -> Module, IMPLICIT_PKL -> Implicit, SEALED_PKL -> Sealed, diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 78ee32b98c3c..ffbae59bb79a 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -296,7 +296,7 @@ class PlainPrinter(_ctx: Context) extends Printer { /** String representation of symbol's definition key word */ protected def keyString(sym: Symbol): String = { val flags = sym.flagsUNSAFE - if (flags is JavaInterface) "interface" + if (flags is JavaTrait) "interface" else if ((flags is Trait) && !(flags is ImplClass)) "trait" else if (sym.isClass) "class" else if (sym.isType) "type" diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 28d742b5ee27..436d9bcf7d9d 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -144,11 +144,11 @@ object ExplicitOuter { nme.OUTER.expandedName(cls) /** Class needs an outer pointer, provided there is a reference to an outer this in it. */ - def needsOuterIfReferenced(cls: ClassSymbol)(implicit ctx: Context): Boolean = !( - cls.isStatic || - cls.owner.enclosingClass.isStaticOwner || - cls.is(Interface) - ) + def needsOuterIfReferenced(cls: ClassSymbol)(implicit ctx: Context): Boolean = + !(cls.isStatic || + cls.owner.enclosingClass.isStaticOwner || + cls.is(PureInterface) + ) /** Class unconditionally needs an outer pointer. This is the case if * the class needs an outer pointer if referenced and one of the following holds: diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 1d342404a272..3d68a2687c2b 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -134,7 +134,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => case Some(call) => if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil case None => - if (baseCls.is(Interface) || defn.PhantomClasses.contains(baseCls)) Nil + if (baseCls.is(NoInitsTrait) || defn.PhantomClasses.contains(baseCls)) Nil else { //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") superRef(baseCls.primaryConstructor).appliedToNone :: Nil diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 3a1f0a98b461..e8bb1b9e7286 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -492,6 +492,13 @@ class Namer { typer: Typer => index(rest)(inClassContext(selfInfo)) denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo) + if (cls is Trait) { + if (body forall isNoInitMember) { + cls.setFlag(NoInits) + if (body forall isPureInterfaceMember) + cls.setFlag(PureInterface) + } + } } }