From 4a3be27db24c8a28da88d9a79d547849a7b28c2f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Jun 2015 23:08:11 +0200 Subject: [PATCH 01/10] The expected type of a constructor body is Unit Thsi was not true for erased typechecking. --- src/dotty/tools/dotc/transform/Erasure.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 1664db456124..962297517fad 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -479,7 +479,9 @@ object Erasure extends TypeTestsCasts{ val MethodType(pnames, ptypes) = sym.info.resultType effectiveSym = sym.copy(info = MethodType(pnames, ptypes, defn.ObjectType)) } - val restpe = effectiveSym.info.resultType + val restpe = + if (effectiveSym.isConstructor) defn.UnitType + else effectiveSym.info.resultType val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, vparamss = (outer.paramDefs(effectiveSym) ::: ddef.vparamss.flatten) :: Nil, From f3a676a33e075b93d99689f26e76632e57a4d8c3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Jun 2015 23:11:31 +0200 Subject: [PATCH 02/10] Create lazyval bitmaps only after super call. This was one source of the "reference to this before super" errors. --- src/dotty/tools/dotc/transform/LazyVals.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index e52e2537c8dd..b57e4c5926e3 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -68,11 +68,16 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { appendOffsetDefs.get(cls) match { case None => template case Some(data) => - cpy.Template(template)(body = data.defs ::: template.body) + cpy.Template(template)(body = addInFront(data.defs, template.body)) } } + private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match { + case first :: rest if isSuperConstrCall(first) => first :: prefix ::: rest + case _ => prefix ::: stats + } + /** Replace a local lazy val inside a method, * with a LazyHolder from * dotty.runtime(eg dotty.runtime.LazyInt) From 9bf44f867c1a9f4625dd7fac9575c3e74373402b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Jun 2015 23:12:46 +0200 Subject: [PATCH 03/10] Map outer accessors to outer paramaters Map references to outer accessors in secondary constructors to outer parameters. This was the second source of "reference to this before super call" errors. --- src/dotty/tools/dotc/transform/Constructors.scala | 12 ++++++++++++ tests/{pending => }/run/constructors.check | 0 tests/{pending => }/run/constructors.scala | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) rename tests/{pending => }/run/constructors.check (100%) rename tests/{pending => }/run/constructors.scala (91%) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 100e9ff211c8..d078bab1d1bc 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -156,6 +156,16 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val constrStats, clsStats = new mutable.ListBuffer[Tree] + /** Map outer getters $outer and outer accessors $A$B$$$outer to the given outer parameter. */ + def mapOuter(outerParam: Symbol) = new TreeMap { + override def transform(tree: Tree)(implicit ctx: Context) = tree match { + case Apply(fn, Nil) if fn.symbol.is(OuterAccessor) || fn.symbol.isGetter && fn.symbol.name == nme.OUTER => + ref(outerParam) + case _ => + super.transform(tree) + } + } + // Split class body into statements that go into constructor and // definitions that are kept as members of the class. def splitStats(stats: List[Tree]): Unit = stats match { @@ -174,6 +184,8 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor owner = constr.symbol).installAfter(thisTransform) constrStats += intoConstr(stat, sym) } + case DefDef(nme.CONSTRUCTOR, _, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) => + clsStats += mapOuter(outerParam.symbol).transform(stat) case _: DefTree => clsStats += stat case _ => diff --git a/tests/pending/run/constructors.check b/tests/run/constructors.check similarity index 100% rename from tests/pending/run/constructors.check rename to tests/run/constructors.check diff --git a/tests/pending/run/constructors.scala b/tests/run/constructors.scala similarity index 91% rename from tests/pending/run/constructors.scala rename to tests/run/constructors.scala index 90926431f967..19afc3d678e3 100644 --- a/tests/pending/run/constructors.scala +++ b/tests/run/constructors.scala @@ -4,8 +4,9 @@ class A(x: Int, y: Int) { def this(x: Int) = this(x, x); def this() = this(1); override def toString() = "x=" + x + " y=" + y; - class B(a: Int, b: Int, c: String) { + class B(val a: Int, b: Int, c: String) { def this(str: String) = this(x, y, str); + val xx = a override def toString() = "x=" + x + " y=" + y + " a=" + a + " b=" + b + " c=" + c; } From c093792189f49c3e72ada99ca0fdb97e4023ef78 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 20 Jun 2015 00:43:28 +0200 Subject: [PATCH 04/10] Map proxy references in constructors to parameters Proxy references in constructors can't be left as fields because they can happen before the supercall. --- src/dotty/tools/dotc/transform/LambdaLift.scala | 17 +++++++++++++++-- tests/{pending => }/run/shortClass.check | 0 tests/{pending => }/run/shortClass.scala | 0 3 files changed, 15 insertions(+), 2 deletions(-) rename tests/{pending => }/run/shortClass.check (100%) rename tests/{pending => }/run/shortClass.scala (100%) diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 41d6f3c438c4..2f06190c3375 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -391,11 +391,24 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform val freeParamDefs = ownProxies.map(proxy => transformFollowingDeep(ValDef(proxy.asTerm).withPos(tree.pos)).asInstanceOf[ValDef]) def proxyInit(field: Symbol, param: Symbol) = - transformFollowingDeep(ref(field).becomes(ref(param))) + transformFollowingDeep(memberRef(field).becomes(ref(param))) + + /** Map references to proxy fields `this.proxy` to procy parameers */ + def mapProxies = new TreeMap { + override def transform(tree: Tree)(implicit ctx: Context) = tree match { + case Select(This(_), _) if proxies contains tree.symbol => + ref(tree.symbol.subst(proxies, ownProxies)) + case _ => + super.transform(tree) + } + } + + /** Initialize proxy fields from proxy parameters and map `rhs` from fields to parameters */ def copyParams(rhs: Tree) = { ctx.log(i"copy params ${proxies.map(_.showLocated)}%, %, own = ${ownProxies.map(_.showLocated)}%, %") - seq((proxies, ownProxies).zipped.map(proxyInit), rhs) + seq((proxies, ownProxies).zipped.map(proxyInit), mapProxies.transform(rhs)) } + tree match { case tree: DefDef => cpy.DefDef(tree)( diff --git a/tests/pending/run/shortClass.check b/tests/run/shortClass.check similarity index 100% rename from tests/pending/run/shortClass.check rename to tests/run/shortClass.check diff --git a/tests/pending/run/shortClass.scala b/tests/run/shortClass.scala similarity index 100% rename from tests/pending/run/shortClass.scala rename to tests/run/shortClass.scala From 9a4b5e7c306eb3f1c82ace10dd62576473b1dec1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 20 Jun 2015 00:44:47 +0200 Subject: [PATCH 05/10] Map outer fields to parameters in primary constructor Previously this was only done in secondary constructors; need to do it in primary constructor as well to avoid "reference to this before super" problems. --- src/dotty/tools/dotc/transform/Constructors.scala | 8 +++++++- tests/{pending => }/run/NestedClasses.check | 0 tests/{pending => }/run/NestedClasses.scala | 0 tests/{pending => }/run/t8611b.flags | 0 tests/{pending => }/run/t8611b.scala | 0 5 files changed, 7 insertions(+), 1 deletion(-) rename tests/{pending => }/run/NestedClasses.check (100%) rename tests/{pending => }/run/NestedClasses.scala (100%) rename tests/{pending => }/run/t8611b.flags (100%) rename tests/{pending => }/run/t8611b.scala (100%) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index d078bab1d1bc..3d7af6bf5c37 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -233,9 +233,15 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor case stats => (Nil, stats) } + val mappedSuperCalls = vparams match { + case (outerParam @ ValDef(nme.OUTER, _, _)) :: _ => + superCalls.map(mapOuter(outerParam.symbol).transform) + case _ => superCalls + } + cpy.Template(tree)( constr = cpy.DefDef(constr)( - rhs = Block(superCalls ::: copyParams ::: followConstrStats, unitLiteral)), + rhs = Block(mappedSuperCalls ::: copyParams ::: followConstrStats, unitLiteral)), body = clsStats.toList) } } diff --git a/tests/pending/run/NestedClasses.check b/tests/run/NestedClasses.check similarity index 100% rename from tests/pending/run/NestedClasses.check rename to tests/run/NestedClasses.check diff --git a/tests/pending/run/NestedClasses.scala b/tests/run/NestedClasses.scala similarity index 100% rename from tests/pending/run/NestedClasses.scala rename to tests/run/NestedClasses.scala diff --git a/tests/pending/run/t8611b.flags b/tests/run/t8611b.flags similarity index 100% rename from tests/pending/run/t8611b.flags rename to tests/run/t8611b.flags diff --git a/tests/pending/run/t8611b.scala b/tests/run/t8611b.scala similarity index 100% rename from tests/pending/run/t8611b.scala rename to tests/run/t8611b.scala From 91e69cbf65821bfe0346d0d0960f4b211c2dc849 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 20 Jun 2015 00:56:45 +0200 Subject: [PATCH 06/10] Fix mapOuter to be type-preserving Only outer references that point to the same class as the outer parameter should be mapped. Other outer references should be left alone. Such references can appear after the initial supercall of a constructor. --- src/dotty/tools/dotc/transform/Constructors.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 3d7af6bf5c37..1d94b3552c0e 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -159,7 +159,11 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor /** Map outer getters $outer and outer accessors $A$B$$$outer to the given outer parameter. */ def mapOuter(outerParam: Symbol) = new TreeMap { override def transform(tree: Tree)(implicit ctx: Context) = tree match { - case Apply(fn, Nil) if fn.symbol.is(OuterAccessor) || fn.symbol.isGetter && fn.symbol.name == nme.OUTER => + case Apply(fn, Nil) + if (fn.symbol.is(OuterAccessor) + || fn.symbol.isGetter && fn.symbol.name == nme.OUTER + ) && + fn.symbol.info.resultType.classSymbol == outerParam.info.classSymbol => ref(outerParam) case _ => super.transform(tree) From 2deaea3481e1825357a97e3d03e403d8f0b38478 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 20 Jun 2015 10:31:09 +0200 Subject: [PATCH 07/10] Updated check file Difference was in generated number for anonymous classes only. --- tests/run/shortClass.check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run/shortClass.check b/tests/run/shortClass.check index fbdb725ccaa0..6ecee9a31877 100644 --- a/tests/run/shortClass.check +++ b/tests/run/shortClass.check @@ -1,7 +1,7 @@ bippity.bop.Foo bippity.bop.Foo$Bar bippity.bop.Foo$Bar$ -Test$$anon$1 +Test$$anon$5 Test$$anon$2 Foo Bar From 2550f4161c1e6f9caa014bfd48d22a29082b71c8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 20 Jun 2015 11:01:19 +0200 Subject: [PATCH 08/10] Avoid noise introduced by printing anonymous class numbers We seem to have an instability where the testing framework influences what numbers anonymous classes are given. We have to deal with this, but not now. For the moment I am masking numbers from the test output. --- tests/run/shortClass.check | 4 ++-- tests/run/shortClass.scala | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/run/shortClass.check b/tests/run/shortClass.check index 6ecee9a31877..0c62fb26f28f 100644 --- a/tests/run/shortClass.check +++ b/tests/run/shortClass.check @@ -1,8 +1,8 @@ bippity.bop.Foo bippity.bop.Foo$Bar bippity.bop.Foo$Bar$ -Test$$anon$5 -Test$$anon$2 +Test$$anon$ +Test$$anon$ Foo Bar Bar$ diff --git a/tests/run/shortClass.scala b/tests/run/shortClass.scala index b7bb0168963a..80b791aa4156 100644 --- a/tests/run/shortClass.scala +++ b/tests/run/shortClass.scala @@ -15,10 +15,12 @@ object Test { import bippity._ import bop._ + def printSanitized(x: String) = println(x)//.filterNot(_.isDigit)) + def main(args: Array[String]): Unit = { val f = new Foo val instances = List(f, new f.Bar, f.Bar, new Foo with DingDongBippy, new f.Bar with DingDongBippy) - instances map (_.getClass.getName) foreach println - instances map shortClassOfInstance foreach println + instances map (_.getClass.getName) foreach printSanitized + instances map shortClassOfInstance foreach printSanitized } } From 5057418d6c16115641e0047ef5c812ce5014e242 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 20 Jun 2015 13:07:14 +0200 Subject: [PATCH 09/10] Re-enable sanitized printing Ws disabled by accident. --- tests/run/shortClass.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run/shortClass.scala b/tests/run/shortClass.scala index 80b791aa4156..c5c2043f4dd0 100644 --- a/tests/run/shortClass.scala +++ b/tests/run/shortClass.scala @@ -15,7 +15,7 @@ object Test { import bippity._ import bop._ - def printSanitized(x: String) = println(x)//.filterNot(_.isDigit)) + def printSanitized(x: String) = println(x.filterNot(_.isDigit)) def main(args: Array[String]): Unit = { val f = new Foo From 8d8942a843b86e50eebea4016d65f40e94e93e9c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 22 Jun 2015 11:22:48 +0200 Subject: [PATCH 10/10] Fix typo --- src/dotty/tools/dotc/transform/LambdaLift.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index 2f06190c3375..043c92737fdc 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -393,7 +393,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform def proxyInit(field: Symbol, param: Symbol) = transformFollowingDeep(memberRef(field).becomes(ref(param))) - /** Map references to proxy fields `this.proxy` to procy parameers */ + /** Map references to proxy fields `this.proxy` to proxy parameters */ def mapProxies = new TreeMap { override def transform(tree: Tree)(implicit ctx: Context) = tree match { case Select(This(_), _) if proxies contains tree.symbol => @@ -408,7 +408,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform ctx.log(i"copy params ${proxies.map(_.showLocated)}%, %, own = ${ownProxies.map(_.showLocated)}%, %") seq((proxies, ownProxies).zipped.map(proxyInit), mapProxies.transform(rhs)) } - + tree match { case tree: DefDef => cpy.DefDef(tree)(