From 66fa1f22ac058e87350304388eca17aedc1e4b64 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 24 Jan 2013 00:35:19 +0100 Subject: Broader checks for poisonous this references. Replaces more VerifyErrors with implementation restrictions. --- .../scala/tools/nsc/transform/ExplicitOuter.scala | 64 ++++++++++++++++------ .../scala/tools/nsc/transform/LambdaLift.scala | 25 +++------ 2 files changed, 56 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 1003d417f6..a86d8aa2d6 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -8,6 +8,7 @@ package transform import symtab._ import Flags.{ CASE => _, _ } +import scala.collection.mutable import scala.collection.mutable.ListBuffer import matching.{ Patterns, ParallelMatching } @@ -209,6 +210,8 @@ abstract class ExplicitOuter extends InfoTransform /** The first outer selection from currently transformed tree. * The result is typed but not positioned. + * + * Will return `EmptyTree` if there is no outer accessor because of a premature self reference. */ protected def outerValue: Tree = if (outerParam != NoSymbol) ID(outerParam) @@ -218,25 +221,34 @@ abstract class ExplicitOuter extends InfoTransform * The result is typed but not positioned. * If the outer access is from current class and current class is final * take outer field instead of accessor + * + * Will return `EmptyTree` if there is no outer accessor because of a premature self reference. */ private def outerSelect(base: Tree): Tree = { - val outerAcc = outerAccessor(base.tpe.typeSymbol.toInterface) - val currentClass = this.currentClass //todo: !!! if this line is removed, we get a build failure that protected$currentClass need an override modifier - // outerFld is the $outer field of the current class, if the reference can - // use it (i.e. reference is allowed to be of the form this.$outer), - // otherwise it is NoSymbol - val outerFld = - if (outerAcc.owner == currentClass && + val baseSym = base.tpe.typeSymbol.toInterface + val outerAcc = outerAccessor(baseSym) + if (outerAcc == NoSymbol && baseSym.ownersIterator.exists(isUnderConstruction)) { + // e.g neg/t6666.scala + // The caller will report the error with more information. + EmptyTree + } else { + val currentClass = this.currentClass //todo: !!! if this line is removed, we get a build failure that protected$currentClass need an override modifier + // outerFld is the $outer field of the current class, if the reference can + // use it (i.e. reference is allowed to be of the form this.$outer), + // otherwise it is NoSymbol + val outerFld = + if (outerAcc.owner == currentClass && base.tpe =:= currentClass.thisType && outerAcc.owner.isEffectivelyFinal) - outerField(currentClass) suchThat (_.owner == currentClass) - else - NoSymbol - val path = - if (outerFld != NoSymbol) Select(base, outerFld) - else Apply(Select(base, outerAcc), Nil) - - localTyper typed path + outerField(currentClass) suchThat (_.owner == currentClass) + else + NoSymbol + val path = + if (outerFld != NoSymbol) Select(base, outerFld) + else Apply(Select(base, outerAcc), Nil) + + localTyper typed path + } } /** The path @@ -256,6 +268,17 @@ abstract class ExplicitOuter extends InfoTransform else outerPath(outerSelect(base), from.outerClass, to) } + + /** The stack of constructor symbols in which a call to this() or to the super + * constructor is active. + */ + protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls exists (_.owner == clazz) + protected val selfOrSuperCalls = mutable.Stack[Symbol]() + @inline protected def inSelfOrSuperCall[A](sym: Symbol)(a: => A) = { + selfOrSuperCalls push sym + try a finally selfOrSuperCalls.pop() + } + override def transform(tree: Tree): Tree = { val savedOuterParam = outerParam try { @@ -269,7 +292,10 @@ abstract class ExplicitOuter extends InfoTransform } case _ => } - super.transform(tree) + if (treeInfo isSelfOrSuperConstrCall tree) // TODO also handle (and test) (treeInfo isEarlyDef tree) + inSelfOrSuperCall(currentOwner)(super.transform(tree)) + else + super.transform(tree) } finally outerParam = savedOuterParam } @@ -335,7 +361,8 @@ abstract class ExplicitOuter extends InfoTransform /** The definition tree of the outer accessor of current class */ - def outerFieldDef: Tree = VAL(outerField(currentClass)) === EmptyTree + def outerFieldDef: Tree = + VAL(outerField(currentClass)) === EmptyTree /** The definition tree of the outer accessor of current class */ @@ -483,6 +510,9 @@ abstract class ExplicitOuter extends InfoTransform val clazz = sym.owner val vparamss1 = if (isInner(clazz)) { // (4) + if (isUnderConstruction(clazz.outerClass)) { + reporter.error(tree.pos, s"Implementation restriction: ${clazz.fullLocationString} requires premature access to ${clazz.outerClass}.") + } val outerParam = sym.newValueParameter(nme.OUTER, sym.pos) setInfo clazz.outerClass.thisType ((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index a4f75f424f..d912b76f68 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -317,7 +317,7 @@ abstract class LambdaLift extends InfoTransform { else searchIn(currentOwner) } - private def memberRef(sym: Symbol) = { + private def memberRef(sym: Symbol): Tree = { val clazz = sym.owner.enclClass //Console.println("memberRef from "+currentClass+" to "+sym+" in "+clazz) def prematureSelfReference() { @@ -331,12 +331,17 @@ abstract class LambdaLift extends InfoTransform { if (clazz == currentClass) gen.mkAttributedThis(clazz) else { sym resetFlag (LOCAL | PRIVATE) - if (selfOrSuperCalls exists (_.owner == clazz)) { + if (isUnderConstruction(clazz)) { prematureSelfReference() EmptyTree } else if (clazz.isStaticOwner) gen.mkAttributedQualifier(clazz.thisType) - else outerPath(outerValue, currentClass.outerClass, clazz) + else { + outerValue match { + case EmptyTree => prematureSelfReference(); return EmptyTree + case o => outerPath(o, currentClass.outerClass, clazz) + } + } } Select(qual, sym) setType sym.tpe } @@ -507,25 +512,13 @@ abstract class LambdaLift extends InfoTransform { private def preTransform(tree: Tree) = super.transform(tree) setType lifted(tree.tpe) - /** The stack of constructor symbols in which a call to this() or to the super - * constructor is active. - */ - private val selfOrSuperCalls = mutable.Stack[Symbol]() - @inline private def inSelfOrSuperCall[A](sym: Symbol)(a: => A) = try { - selfOrSuperCalls push sym - a - } finally selfOrSuperCalls.pop() - override def transform(tree: Tree): Tree = tree match { case Select(ReferenceToBoxed(idt), elem) if elem == nme.elem => postTransform(preTransform(idt), isBoxedRef = false) case ReferenceToBoxed(idt) => postTransform(preTransform(idt), isBoxedRef = true) case _ => - def transformTree = postTransform(preTransform(tree)) - if (treeInfo isSelfOrSuperConstrCall tree) - inSelfOrSuperCall(currentOwner)(transformTree) - else transformTree + postTransform(preTransform(tree)) } /** Transform statements and add lifted definitions to them. */ -- cgit v1.2.3