From 065a0b4c9cb34b448e070a80e4ad4af9e5ade20a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Jun 2015 16:08:50 +0200 Subject: [PATCH 01/11] Implementations of mixin getters are never ParamAccessors Need to suppress the flag when copying symbols. --- src/dotty/tools/dotc/transform/MixinOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala index 3685a00fc25c..766114451a9e 100644 --- a/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -17,7 +17,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: val res = member.copy( owner = cls, name = member.name.stripScala2LocalSuffix, - flags = member.flags &~ Deferred, + flags = member.flags &~ (Deferred | ParamAccessor), info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm res.addAnnotations(member.annotations) res From 8c28293fd52213616ed8ca3a2f600eb1e19debe0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Jun 2015 19:09:06 +0200 Subject: [PATCH 02/11] Implement checking for illegal parent trait constructor calls. A parent trait may not be parameterized (as in T()) if the calling class does not directly implement that trait. --- src/dotty/tools/dotc/typer/Checking.scala | 14 +++++++++++--- src/dotty/tools/dotc/typer/Typer.scala | 3 +-- test/dotc/tests.scala | 1 + tests/neg/traitParamsTyper.scala | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 tests/neg/traitParamsTyper.scala diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 3ef6d059a0f4..9047b8cb35c0 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -20,6 +20,7 @@ import annotation.unchecked import util.Positions._ import util.{Stats, SimpleMap} import util.common._ +import transform.SymUtils._ import Decorators._ import Uniques._ import ErrorReporting.{err, errorType, DiagnosticString} @@ -328,9 +329,15 @@ trait Checking { } } - def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = { - ??? // to be done in later phase: check that class `cls` is legal in a new. - } + def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = + if (!ctx.isAfterTyper) { + val called = call.tpe.classSymbol + if (caller is Trait) + ctx.error(i"$caller may not call constructor of $called", call.pos) + else if (called.is(Trait) && !caller.mixins.contains(called)) + ctx.error(i"""$called is already implemented by super${caller.superClass}, + |its constructor cannot be called again""".stripMargin, call.pos) + } } trait NoChecking extends Checking { @@ -343,4 +350,5 @@ trait NoChecking extends Checking { override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () + override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 5f03d19e7c62..2bdd0d19714e 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -911,8 +911,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (tree.isType) typedType(tree)(superCtx) else { val result = typedExpr(tree)(superCtx) - if ((cls is Trait) && result.tpe.classSymbol.isRealClass && !ctx.isAfterTyper) - ctx.error(s"trait may not call constructor of ${result.tpe.classSymbol}", tree.pos) + checkParentCall(result, cls) result } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 0a6127580df9..ec5e34574c33 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -138,6 +138,7 @@ class tests extends CompilerTest { @Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8) @Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5) @Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2) + @Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5) @Test def run_all = runFiles(runDir) diff --git a/tests/neg/traitParamsTyper.scala b/tests/neg/traitParamsTyper.scala new file mode 100644 index 000000000000..f87ba3691d7f --- /dev/null +++ b/tests/neg/traitParamsTyper.scala @@ -0,0 +1,16 @@ +trait T(x: Int) { + def f = x +} + +class C(x: Int) extends T() // error + +trait U extends C with T + +trait V extends C(1) with T(2) // two errors + +trait W extends T(3) // error + + +class E extends T(0) +class F extends E with T(1) // error + From 2813b01235aaebdae1522bc31d69b830e1830b9b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Jun 2015 19:11:33 +0200 Subject: [PATCH 03/11] Implement trait parameters Add necessary logic to Mixin. Also add tests that all parameterized traits are called with parameters set. --- .../tools/dotc/core/SymDenotations.scala | 2 +- src/dotty/tools/dotc/transform/Mixin.scala | 74 +++++++++++++++---- test/dotc/tests.scala | 1 + tests/neg/traitParamsMixin.scala | 12 +++ tests/pos/traitParams.scala | 12 +++ 5 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 tests/neg/traitParamsMixin.scala create mode 100644 tests/pos/traitParams.scala diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 9db75ee94c02..d8dddb0828cb 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -516,7 +516,7 @@ object SymDenotations { !isAnonymousFunction && !isCompanionMethod - /** Is this a setter? */ + /** Is this a getter? */ final def isGetter(implicit ctx: Context) = (this is Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 8562f4f02660..ce3eb6cda936 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -20,7 +20,7 @@ import collection.mutable /** This phase performs the following transformations: * - * 1. (done in `traitDefs`) Map every concrete trait getter + * 1. (done in `traitDefs` and `transformSym`) Map every concrete trait getter * * def x(): T = expr * @@ -46,32 +46,43 @@ 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 getter ` def x(): T` in M, - * in order of textual occurrence, produce the following: + * 3.1 (done in `traitInits`) For every parameter accessor ` def x(): T` in M, + * in order of textual occurrence, add * - * 3.1.1 If `x` is also a member of `C`, and M is a Dotty trait: + * def x() = e + * + * where `e` is the constructor argument in C that corresponds to `x`. Issue + * an error if no such argument exists. + * + * 3.2 (done in `traitInits`) For every concrete trait getter ` def x(): T` in M + * which is not a parameter accessor, in order of textual occurrence, produce the following: + * + * 3.2.1 If `x` is also a member of `C`, and M is a Dotty trait: * * def x(): T = super[M].initial$x() * - * 3.1.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: + * 3.2.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: * * def x(): T = _ * - * 3.1.3 If `x` is not a member of `C`, and M is a Dotty trait: + * 3.2.3 If `x` is not a member of `C`, and M is a Dotty trait: * * super[M].initial$x() * - * 3.1.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. + * 3.2.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. * * - * 3.2 (done in `superCallOpt`) The call: + * 3.3 (done in `superCallOpt`) The call: * * super[M]. * - * 3.3 (done in `setters`) For every concrete setter ` def x_=(y: T)` in M: + * 3.4 (done in `setters`) For every concrete setter ` def x_=(y: T)` in M: * * def x_=(y: T) = () * + * 4. (done in `transformTemplate` and `transformSym`) Drop all parameters from trait + * constructors. + * * 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 @@ -87,6 +98,8 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => 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).ensureNotPrivate + else if (sym.isConstructor && sym.owner.is(Trait) && sym.info.firstParamTypes.nonEmpty) + sym.copySymDenotation(info = MethodType(Nil, sym.info.resultType)) else sym @@ -131,15 +144,22 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => }) ++ initBuf } - def transformSuper(tree: Tree): Tree = { + /** Map constructor call to a pair of a supercall and a list of arguments + * to be used as initializers of trait parameters if the target of the call + * is a trait. + */ + def transformConstructor(tree: Tree): (Tree, List[Tree]) = { val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree - superRef(tree.symbol, tree.pos).appliedToArgs(args) + val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil) + (superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs) } - val superCalls = ( + val superCallsAndArgs = ( for (p <- impl.parents if p.symbol.isConstructor) - yield p.symbol.owner -> transformSuper(p) + yield p.symbol.owner -> transformConstructor(p) ).toMap + val superCalls = superCallsAndArgs.mapValues(_._1) + val initArgs = superCallsAndArgs.mapValues(_._2) def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { case Some(call) => @@ -155,12 +175,31 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def wasDeferred(sym: Symbol) = ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } - def traitInits(mixin: ClassSymbol): List[Tree] = + def traitInits(mixin: ClassSymbol): List[Tree] = { + var argNum = 0 + def nextArgument() = initArgs.get(mixin) match { + case Some(arguments) => + try arguments(argNum) finally argNum += 1 + case None => + val (msg, pos) = impl.parents.find(_.tpe.typeSymbol == mixin) match { + case Some(parent) => ("lacks argument list", parent.pos) + case None => + ("""is indirectly implemented, + |needs to be implemented directly so that arguments can be passed""".stripMargin, + cls.pos) + } + ctx.error(i"parameterized $mixin $msg", pos) + EmptyTree + } for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { val isScala2x = mixin.is(Scala2x) def default = Underscore(getter.info.resultType) def initial = transformFollowing(superRef(initializer(getter)).appliedToNone) - if (isCurrent(getter) || getter.is(ExpandedName)) + if (isCurrent(getter) || getter.is(ExpandedName)) { + val rhs = + if (getter.is(ParamAccessor)) nextArgument() + else if (isScala2x) Underscore(getter.info.resultType) + else transformFollowing(superRef(initializer(getter)).appliedToNone) // transformFollowing call is needed to make memoize & lazy vals run transformFollowing( DefDef(implementation(getter.asTerm), @@ -178,12 +217,17 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => else if (isScala2x) EmptyTree else initial } + } def setters(mixin: ClassSymbol): List[Tree] = for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos)) cpy.Template(impl)( + constr = + if (cls.is(Trait) && impl.constr.vparamss.flatten.nonEmpty) + cpy.DefDef(impl.constr)(vparamss = Nil :: Nil) + else impl.constr, parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), body = if (cls is Trait) traitDefs(impl.body) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index ec5e34574c33..1aa35e3ee66b 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -139,6 +139,7 @@ class tests extends CompilerTest { @Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5) @Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2) @Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5) + @Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2) @Test def run_all = runFiles(runDir) diff --git a/tests/neg/traitParamsMixin.scala b/tests/neg/traitParamsMixin.scala new file mode 100644 index 000000000000..dfb9fbe2f66d --- /dev/null +++ b/tests/neg/traitParamsMixin.scala @@ -0,0 +1,12 @@ +trait T(x: Int) { + def f = x +} + +class C extends T // error + +trait U extends T + +class D extends U { // error + +} + diff --git a/tests/pos/traitParams.scala b/tests/pos/traitParams.scala new file mode 100644 index 000000000000..a53fbfe45b36 --- /dev/null +++ b/tests/pos/traitParams.scala @@ -0,0 +1,12 @@ +trait T(x: Int) { + def f = x +} + +trait U extends T + +class C extends U with T(2) { + +} + +class D extends C with T + From d19c30cbf691f55c471aebeb906435dc1824691e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Jun 2015 19:28:09 +0200 Subject: [PATCH 04/11] Convert pos test to be a run test. --- tests/pos/traitParams.scala | 12 ------------ tests/run/traitParams.scala | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 12 deletions(-) delete mode 100644 tests/pos/traitParams.scala create mode 100644 tests/run/traitParams.scala diff --git a/tests/pos/traitParams.scala b/tests/pos/traitParams.scala deleted file mode 100644 index a53fbfe45b36..000000000000 --- a/tests/pos/traitParams.scala +++ /dev/null @@ -1,12 +0,0 @@ -trait T(x: Int) { - def f = x -} - -trait U extends T - -class C extends U with T(2) { - -} - -class D extends C with T - diff --git a/tests/run/traitParams.scala b/tests/run/traitParams.scala new file mode 100644 index 000000000000..7b1ddbd54f7a --- /dev/null +++ b/tests/run/traitParams.scala @@ -0,0 +1,19 @@ +trait T(x: Int, val y: Int) { + def f = x +} + +trait U extends T { + override def f = super.f + y +} + +class C(x: Int) extends U with T(x, x * x) + +class D extends C(10) with T { + +} + +object Test { + def main(args: Array[String]): Unit = + assert(new D().f == 110) +} + From da773fb91df35684034f106544fe59f197b4ec80 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 12 Jun 2015 11:09:19 +0200 Subject: [PATCH 05/11] Extend test to verify initialization order Verify that the initilialization order described in #640 is correctly implemented. --- tests/run/traitParams.scala | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/run/traitParams.scala b/tests/run/traitParams.scala index 7b1ddbd54f7a..c1b77cf37e32 100644 --- a/tests/run/traitParams.scala +++ b/tests/run/traitParams.scala @@ -1,19 +1,26 @@ +object State { + var s: Int = 0 +} + trait T(x: Int, val y: Int) { def f = x } trait U extends T { + State.s += 1 override def f = super.f + y } +import State._ +class C(x: Int) extends U with T(x, x * x + s) +class C2(x: Int) extends T(x, x * x + s) with U -class C(x: Int) extends U with T(x, x * x) - -class D extends C(10) with T { - -} +class D extends C(10) with T +class D2 extends C2(10) with T object Test { - def main(args: Array[String]): Unit = + def main(args: Array[String]): Unit = { assert(new D().f == 110) + assert(new D2().f == 111) + } } From ece6ed49412802909a0e57405331a834cff8d6dc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 12 Jun 2015 17:52:10 +0200 Subject: [PATCH 06/11] Revert: Implementations of mixin getters are never ParamAccessors (reverted from commit 2c559003534d4441688bcf1664e53993c6187bde) Mixin should drop the ParamAccessor flag anyway in traits, so there is no need to mask it when creating implementations of trait definitions. --- src/dotty/tools/dotc/transform/MixinOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala index 766114451a9e..3685a00fc25c 100644 --- a/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/src/dotty/tools/dotc/transform/MixinOps.scala @@ -17,7 +17,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: val res = member.copy( owner = cls, name = member.name.stripScala2LocalSuffix, - flags = member.flags &~ (Deferred | ParamAccessor), + flags = member.flags &~ Deferred, info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm res.addAnnotations(member.annotations) res From 0de976828e54b2e407f84bd098aea950ef52dbf1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 12 Jun 2015 17:57:27 +0200 Subject: [PATCH 07/11] Drop parameter accessor flag in traits Parameter accessors in traits have the ParamAccessor flag removed in the Mixin transformSym method. --- src/dotty/tools/dotc/transform/Mixin.scala | 10 ++++++---- tests/run/traitParams.scala | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index ce3eb6cda936..7319d10cdd48 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -20,7 +20,7 @@ import collection.mutable /** This phase performs the following transformations: * - * 1. (done in `traitDefs` and `transformSym`) Map every concrete trait getter + * 1. (done in `traitDefs` and `transformSym`) Map every concrete trait getter * * def x(): T = expr * @@ -83,6 +83,8 @@ import collection.mutable * 4. (done in `transformTemplate` and `transformSym`) Drop all parameters from trait * constructors. * + * 5. (done in `transformSym`) Drop ParamAccessor flag from all parameter accessors in traits. + * * 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 @@ -97,7 +99,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => 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).ensureNotPrivate + sym.copySymDenotation(initFlags = sym.flags &~ ParamAccessor | Deferred).ensureNotPrivate else if (sym.isConstructor && sym.owner.is(Trait) && sym.info.firstParamTypes.nonEmpty) sym.copySymDenotation(info = MethodType(Nil, sym.info.resultType)) else @@ -124,7 +126,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => 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 && !stat.symbol.is(Flags.Lazy) => + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => // make initializer that has all effects of previous getter, // replace getter rhs with empty tree. val vsym = stat.symbol @@ -197,7 +199,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def initial = transformFollowing(superRef(initializer(getter)).appliedToNone) if (isCurrent(getter) || getter.is(ExpandedName)) { val rhs = - if (getter.is(ParamAccessor)) nextArgument() + if (ctx.atPhase(thisTransform)(implicit ctx => getter.is(ParamAccessor))) nextArgument() else if (isScala2x) Underscore(getter.info.resultType) else transformFollowing(superRef(initializer(getter)).appliedToNone) // transformFollowing call is needed to make memoize & lazy vals run diff --git a/tests/run/traitParams.scala b/tests/run/traitParams.scala index c1b77cf37e32..82c176461c24 100644 --- a/tests/run/traitParams.scala +++ b/tests/run/traitParams.scala @@ -10,6 +10,12 @@ trait U extends T { State.s += 1 override def f = super.f + y } +trait U2(a: Any) extends T { + def d = a // okay + val v = a // okay + a // used to crash +} + import State._ class C(x: Int) extends U with T(x, x * x + s) class C2(x: Int) extends T(x, x * x + s) with U From 321f04e9caf04589320a99a897441d4e1baefff9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 12 Jun 2015 17:56:06 +0200 Subject: [PATCH 08/11] More explicit test of trait parameter initialization order As suggested by @retronym --- tests/run/traitParamInit.scala | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/run/traitParamInit.scala diff --git a/tests/run/traitParamInit.scala b/tests/run/traitParamInit.scala new file mode 100644 index 000000000000..37d8a425d5ff --- /dev/null +++ b/tests/run/traitParamInit.scala @@ -0,0 +1,30 @@ +object Trace { + private var results = List[Any]() + def apply[A](a: A) = {results ::= a; a} + def fetchAndClear(): Seq[Any] = try results.reverse finally results = Nil +} +trait T(a: Any) { + val ta = a + Trace(s"T.($ta)") + val t_val = Trace("T.val") +} + +trait U(a: Any) extends T { + val ua = a + Trace(s"U.($ua)") +} + +object Test { + def check(expected: Any) = { + val actual = Trace.fetchAndClear() + if (actual != expected) + sys.error(s"\n$actual\n$expected") + } + def main(args: Array[String]): Unit = { + new T(Trace("ta")) with U(Trace("ua")) {} + check(List("ta", "T.(ta)", "T.val", "ua", "U.(ua)")) + + new U(Trace("ua")) with T(Trace("ta")) {} + check(List("ta", "T.(ta)", "T.val", "ua", "U.(ua)")) + } +} From b8628c13a56db5335249a79ccd7772c3245999db Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 12 Jun 2015 18:57:18 +0200 Subject: [PATCH 09/11] References to trait parameter accessors do not have an elidable prefix. Reason: The ParamAccessor flag will be reset later in Mixin. We do not want to rewrite the references to a trait parameter accessor then, so it's better to generate all references with a `this.` prefix from the start. --- src/dotty/tools/dotc/ast/tpd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index defcf4838f59..51011f90b1fe 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -302,7 +302,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { true case pre: ThisType => pre.cls.isStaticOwner || - tp.symbol.is(ParamOrAccessor) && ctx.owner.enclosingClass == pre.cls + tp.symbol.is(ParamOrAccessor) && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls // was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough // and was spuriously triggered in case inner class would inherit from outer one // eg anonymous TypeMap inside TypeMap.andThen From e4db53a62f3484717098f04cabb0f477401e2940 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Jun 2015 10:47:07 +0200 Subject: [PATCH 10/11] Fixed typo introduced by merge --- src/dotty/tools/dotc/transform/Mixin.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 7319d10cdd48..b4fa103836fc 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -204,7 +204,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => else transformFollowing(superRef(initializer(getter)).appliedToNone) // transformFollowing call is needed to make memoize & lazy vals run transformFollowing( - DefDef(implementation(getter.asTerm), + DefDef(implementation(getter.asTerm), if (isScala2x) { if (getter.is(Flags.Lazy)) { // lazy vals need to have a rhs that will be the lazy initializer val sym = mixin.implClass.info.nonPrivateDecl(getter.name).suchThat(_.info.paramTypess match { @@ -216,6 +216,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => else default } else initial) ) + } else if (isScala2x) EmptyTree else initial } From 0369f1ec8df932e75ce974121083bc3e160aebc2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Jun 2015 12:35:28 +0200 Subject: [PATCH 11/11] Fix merge breakage Lazy Scala2 fields and trait parameters touched the same code in Mixin and the merge dropped essential logic. Also cleaned up some of the code having to do with lazy Scala2 fields. --- src/dotty/tools/dotc/transform/Mixin.scala | 31 ++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index b4fa103836fc..de6cde8f2cc6 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -193,29 +193,32 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => ctx.error(i"parameterized $mixin $msg", pos) EmptyTree } + for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { val isScala2x = mixin.is(Scala2x) def default = Underscore(getter.info.resultType) def initial = transformFollowing(superRef(initializer(getter)).appliedToNone) + + /** A call to the implementation of `getter` in `mixin`'s implementation class */ + def lazyGetterCall = { + def canbeImplClassGetter(sym: Symbol) = sym.info.firstParamTypes match { + case t :: Nil => t.isDirectRef(mixin) + case _ => false + } + val implClassGetter = mixin.implClass.info.nonPrivateDecl(getter.name) + .suchThat(canbeImplClassGetter).symbol + ref(mixin.implClass).select(implClassGetter).appliedTo(This(cls)) + } + if (isCurrent(getter) || getter.is(ExpandedName)) { val rhs = if (ctx.atPhase(thisTransform)(implicit ctx => getter.is(ParamAccessor))) nextArgument() - else if (isScala2x) Underscore(getter.info.resultType) + else if (isScala2x) + if (getter.is(Lazy)) lazyGetterCall + else Underscore(getter.info.resultType) else transformFollowing(superRef(initializer(getter)).appliedToNone) // transformFollowing call is needed to make memoize & lazy vals run - transformFollowing( - DefDef(implementation(getter.asTerm), - if (isScala2x) { - if (getter.is(Flags.Lazy)) { // lazy vals need to have a rhs that will be the lazy initializer - val sym = mixin.implClass.info.nonPrivateDecl(getter.name).suchThat(_.info.paramTypess match { - case List(List(t: TypeRef)) => t.isDirectRef(mixin) - case _ => false - }).symbol // lazy val can be overloaded - ref(mixin.implClass).select(sym).appliedTo(This(ctx.owner.asClass)) - } - else default - } else initial) - ) + transformFollowing(DefDef(implementation(getter.asTerm), rhs)) } else if (isScala2x) EmptyTree else initial