From abe2a47dc08ad888caf2c7c2c365d63b328d01b3 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 27 May 2015 15:20:17 +0200 Subject: [PATCH 1/8] Add a test that tests supercalls in traits. --- tests/run/supercalls-traits.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/run/supercalls-traits.scala diff --git a/tests/run/supercalls-traits.scala b/tests/run/supercalls-traits.scala new file mode 100644 index 000000000000..09e841f46226 --- /dev/null +++ b/tests/run/supercalls-traits.scala @@ -0,0 +1,15 @@ +trait A { + def foo = 1 +} + +trait B { + def foo = 2 +} + +class C extends A with B { + override def foo = super[A].foo + super[B].foo +} + +object Test { + def main(args: Array[String]) = assert(new C().foo == 3) +} From f987eb08e5323ec60856f29fbbd67827f5fee55b Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 27 May 2015 16:43:12 +0200 Subject: [PATCH 2/8] Add a test for trait initialization. --- tests/run/traits-initialization.scala | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/run/traits-initialization.scala diff --git a/tests/run/traits-initialization.scala b/tests/run/traits-initialization.scala new file mode 100644 index 000000000000..92854ee77c1d --- /dev/null +++ b/tests/run/traits-initialization.scala @@ -0,0 +1,25 @@ +trait A { + var str = "" + str = "a" + val s = str += 'A' + str += '1' +} + +trait B extends A { + str += 'b' + override val s = str += 'B' + str += '2' +} + +class D extends A { + str += 'd' + override val s = str += 'D' + str += '3' +} + +object Test extends D with B { + // should only have 2 fields + str += 'E' + def main(args: Array[String]) = assert(str == "aA1dD3bB2E4") + str += '4' +} From 79516efe0e3c43af2a7084bc2c61316ad7879f91 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 27 May 2015 17:09:04 +0200 Subject: [PATCH 3/8] Add super calls to trait initialization test) --- tests/run/traits-initialization.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/run/traits-initialization.scala b/tests/run/traits-initialization.scala index 92854ee77c1d..e1927d6a2193 100644 --- a/tests/run/traits-initialization.scala +++ b/tests/run/traits-initialization.scala @@ -11,15 +11,15 @@ trait B extends A { str += '2' } -class D extends A { +class D(sup: =>String) extends A { str += 'd' override val s = str += 'D' str += '3' } -object Test extends D with B { +object Test extends D({Test.str += "Z"; Test.str}) with B { // should only have 2 fields str += 'E' - def main(args: Array[String]) = assert(str == "aA1dD3bB2E4") + def main(args: Array[String]) = assert(str == "aA1dD3bB2E4", str) str += '4' } From c1f1d2d6e01e3f483dd0b3d3721354d4fe035b1c Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 27 May 2015 17:14:27 +0200 Subject: [PATCH 4/8] workaround https://issues.scala-lang.org/browse/SI-7666 --- tests/run/traits-initialization.scala | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/run/traits-initialization.scala b/tests/run/traits-initialization.scala index e1927d6a2193..2eee3d633308 100644 --- a/tests/run/traits-initialization.scala +++ b/tests/run/traits-initialization.scala @@ -1,6 +1,11 @@ +object store { + var str = "" +} + +import store._ + trait A { - var str = "" - str = "a" + str += "a" val s = str += 'A' str += '1' } @@ -11,15 +16,16 @@ trait B extends A { str += '2' } -class D(sup: =>String) extends A { +class D(sup: Unit) extends A { str += 'd' override val s = str += 'D' str += '3' } -object Test extends D({Test.str += "Z"; Test.str}) with B { + +object Test extends D({str += "Z"}) with B { // should only have 2 fields str += 'E' - def main(args: Array[String]) = assert(str == "aA1dD3bB2E4", str) + def main(args: Array[String]) = assert(str == "ZaA1dD3bB2E4", str) str += '4' } From c6425f0e103194e50576c591ebab87b19c4b83c3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 27 May 2015 19:14:30 +0200 Subject: [PATCH 5/8] Fix bug in typer erasure's transformInfo ()Unit translates to ()BoxedUnit not BoxedUnit. --- src/dotty/tools/dotc/core/TypeErasure.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala index 65bc5e57fd6e..fac795ef837d 100644 --- a/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/src/dotty/tools/dotc/core/TypeErasure.scala @@ -164,7 +164,7 @@ object TypeErasure { else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) else erase.eraseInfo(tp, sym)(erasureCtx) match { case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) => - defn.BoxedUnitClass.typeRef + MethodType(Nil, Nil, defn.BoxedUnitClass.typeRef) case einfo => einfo } From d81fc0395d525f4dd0ec7d1707b3f0249b014edb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 27 May 2015 19:20:32 +0200 Subject: [PATCH 6/8] Mixin: generate a getter in implementing class only if current A class might implement several fields in inherited traits with the same and type. In that case only one getter should be produced, but all initializing expressions have to be executed. --- src/dotty/tools/dotc/transform/Memoize.scala | 6 +++--- src/dotty/tools/dotc/transform/Mixin.scala | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala index d96a52868623..cbde1ef8ae33 100644 --- a/src/dotty/tools/dotc/transform/Memoize.scala +++ b/src/dotty/tools/dotc/transform/Memoize.scala @@ -50,15 +50,15 @@ import Decorators._ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { val sym = tree.symbol - + def newField = ctx.newSymbol( owner = ctx.owner, name = sym.name.asTermName.fieldName, flags = Private | (if (sym is Stable) EmptyFlags else Mutable), info = sym.info.resultType, coord = tree.pos).enteredAfter(thisTransform) - - lazy val field = sym.field.orElse(newField).asTerm + + lazy val field = sym.field.orElse(newField).asTerm if (sym.is(Accessor, butNot = NoFieldNeeded)) if (sym.isGetter) { tree.rhs.changeOwnerAfter(sym, field, thisTransform) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 8470be2efd08..d5c5746f3d87 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -153,7 +153,12 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { // transformFollowing call is needed to make memoize & lazy vals run - transformFollowing(DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone)) + val rhs = transformFollowing(superRef(initializer(getter)).appliedToNone) + val isCurrent = ctx.atPhase(thisTransform) { implicit ctx => + cls.info.member(getter.name).suchThat(_.isGetter).symbol == getter + } + if (isCurrent) transformFollowing(DefDef(implementation(getter.asTerm), rhs)) + else rhs } def setters(mixin: ClassSymbol): List[Tree] = From 05679f3afccc2a3085bc089bccbdc03096fb90a0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 27 May 2015 19:21:23 +0200 Subject: [PATCH 7/8] Extended test case. Also added non-unit fields and a class that directly implements two traits with the same fields. --- src/dotty/tools/dotc/transform/ResolveSuper.scala | 4 ++-- tests/run/traits-initialization.scala | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ResolveSuper.scala b/src/dotty/tools/dotc/transform/ResolveSuper.scala index 9c0e342569ef..0f1c448d9956 100644 --- a/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -83,7 +83,7 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th 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) && + meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && !isOverridden(meth) && !meth.allOverriddenSymbols.forall(_ is Deferred) for (meth <- mixin.info.decls.toList if needsDisambiguation(meth)) @@ -107,5 +107,5 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th else ddef } - private val PrivateOrDeferred = Private | Deferred + private val PrivateOrAccessorOrDeferred = Private | Accessor | Deferred } diff --git a/tests/run/traits-initialization.scala b/tests/run/traits-initialization.scala index 2eee3d633308..fdddc8f44468 100644 --- a/tests/run/traits-initialization.scala +++ b/tests/run/traits-initialization.scala @@ -6,22 +6,32 @@ import store._ trait A { str += "a" + val i = 2 val s = str += 'A' str += '1' } trait B extends A { str += 'b' + override val i = 3 override val s = str += 'B' str += '2' } class D(sup: Unit) extends A { str += 'd' + override val i = 4 override val s = str += 'D' str += '3' } +class E(sup: Unit) extends A with B { + str += 'd' + override val i = 5 + override val s = str += 'E' + str += '3' +} + object Test extends D({str += "Z"}) with B { // should only have 2 fields From 7779b8fefa745e8b034bc33f46d21bd8599ac4a0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 28 May 2015 10:18:13 +0200 Subject: [PATCH 8/8] Refine definition of isCurrent Fixes problem with run/t261.scala. --- src/dotty/tools/dotc/transform/Mixin.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index d5c5746f3d87..d2e7943f82b4 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -152,9 +152,10 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitInits(mixin: ClassSymbol): List[Tree] = for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { - // transformFollowing call is needed to make memoize & lazy vals run + // transformFollowing call is needed to make memoize & lazy vals run val rhs = transformFollowing(superRef(initializer(getter)).appliedToNone) - val isCurrent = ctx.atPhase(thisTransform) { implicit ctx => + // isCurrent: getter is a member of implementing class + val isCurrent = getter.is(ExpandedName) || ctx.atPhase(thisTransform) { implicit ctx => cls.info.member(getter.name).suchThat(_.isGetter).symbol == getter } if (isCurrent) transformFollowing(DefDef(implementation(getter.asTerm), rhs))