Skip to content

Fix access of lazy vals in traits compiled by 2.12 #3070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
26 changes: 16 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/LinkScala2Impls.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
17 changes: 17 additions & 0 deletions tests/run/scala2trait-lazyval.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
}