From 195840ab3b58658d09b19ad53d0ed36194d3c79b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 5 Sep 2017 18:47:36 +0200 Subject: [PATCH] Fix access of lazy vals in traits compiled by 2.12 This was a case we did not consider before. Accessing a lazy val defined in a Scala 2.12 trait accessed an implementation class which did not exist. need special treatment to avoid this. P.S. It might be preferable to change lazy vals to not generate implementation class references in the first place. I don't know enough about its internals to be able to say whether that's feasible. --- .../src/dotty/tools/dotc/core/NameOps.scala | 6 +++++ .../dotc/transform/AugmentScala2Traits.scala | 2 +- .../dotc/transform/LinkScala2Impls.scala | 26 ++++++++++++------- .../dotty/tools/dotc/transform/SymUtils.scala | 3 +++ tests/run/scala2trait-lazyval.scala | 17 ++++++++++++ 5 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 tests/run/scala2trait-lazyval.scala diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index c429f4e32882..142dd0d1f6d9 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -132,6 +132,12 @@ object NameOps { def implClassName: N = likeSpaced(name ++ tpnme.IMPL_CLASS_SUFFIX) + def traitOfImplClassName: N = { + val suffix = tpnme.IMPL_CLASS_SUFFIX.toString + assert(name.endsWith(suffix), name) + likeSpaced(name.mapLast(_.dropRight(suffix.length))) + } + def errorName: N = likeSpaced(name ++ nme.ERROR) /** Map variance value -1, +1 to 0, 1 */ diff --git a/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala b/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala index 1cca3fea5af5..c5226f756d63 100644 --- a/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala +++ b/compiler/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala @@ -55,7 +55,7 @@ class AugmentScala2Traits extends MiniPhaseTransform with IdentityDenotTransform val implClass = ctx.newCompleteClassSymbol( owner = mixin.owner, name = mixin.name.implClassName, - flags = Abstract | Scala2x, + flags = Abstract | Scala2x | ImplClass, parents = defn.ObjectType :: Nil, assocFile = mixin.assocFile).enteredAfter(thisTransform) diff --git a/compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala b/compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala index e4697a3247c1..180e48e5f84b 100644 --- a/compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala +++ b/compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala @@ -85,23 +85,29 @@ class LinkScala2Impls extends MiniPhase with IdentityDenotTransformer { thisTran val impl = implMethod(sel.symbol) if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withPos(app.pos) else app // could have been an abstract method in a trait linked to from a super constructor + case Apply(sel, args) + if sel.symbol.maybeOwner.is(ImplClass) && sel.symbol.owner.traitOfImplClass.is(Scala_2_12_Trait) => + val impl = implMethod(sel.symbol) + cpy.Apply(app)(ref(impl), args) case _ => app } } + /** The 2.12 implementation method of a super call or implementation class target */ private def implMethod(meth: Symbol)(implicit ctx: Context): Symbol = { - val (implInfo, implName) = - if (meth.owner.is(Scala_2_12_Trait)) - (meth.owner.info, ImplMethName(meth.name.asTermName)) + val implName = ImplMethName(meth.name.asTermName) + val cls = meth.owner + if (cls.is(ImplClass)) + cls.traitOfImplClass.info.decl(implName).atSignature(meth.signature).symbol + else if (cls.is(Scala_2_12_Trait)) + if (meth.isConstructor) + cls.info.decl(nme.TRAIT_CONSTRUCTOR).symbol else - (meth.owner.implClass.info, meth.name) - if (meth.isConstructor) - implInfo.decl(nme.TRAIT_CONSTRUCTOR).symbol - else - implInfo.decl(implName) - .suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature) - .symbol + cls.info.decl(implName) + .suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature) + .symbol + else throw new AssertionError(i"no impl method for $meth") } } diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 4f35c5e614ae..45552fea1131 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -115,6 +115,9 @@ class SymUtils(val self: Symbol) extends AnyVal { def implClass(implicit ctx: Context): Symbol = self.owner.info.decl(self.name.implClassName).symbol + def traitOfImplClass(implicit ctx: Context): Symbol = + self.owner.info.decl(self.name.traitOfImplClassName).symbol + def annotationsCarrying(meta: ClassSymbol)(implicit ctx: Context): List[Annotation] = self.annotations.filter(_.symbol.hasAnnotation(meta)) diff --git a/tests/run/scala2trait-lazyval.scala b/tests/run/scala2trait-lazyval.scala new file mode 100644 index 000000000000..b4c492166b52 --- /dev/null +++ b/tests/run/scala2trait-lazyval.scala @@ -0,0 +1,17 @@ +class Foo extends scala.collection.SeqView[Int, List[Int]] { + def iterator: Iterator[Int] = null + def apply(idx: Int): Int = idx + def length: Int = 0 + protected def underlying = null +} + +object Test { + def main(args: Array[String]): Unit = { + val f: scala.collection.TraversableViewLike[Int, List[Int], _] = new Foo + new f.Transformed[Int] { + def foreach[U](f: Int => U): Unit = () + // underlying is a lazy val + assert(underlying == null) + } + } +}