From 4c34280e5bb3e48db35d97d890e4f5f1c5fb3a26 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 28 Jan 2013 09:26:31 +0100 Subject: Add a test case from the comments of SI-6666. This one lands in the new implementation restriction which beats the VerifyError. --- test/files/neg/t6666b.check | 4 ++++ test/files/neg/t6666b.scala | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 test/files/neg/t6666b.check create mode 100644 test/files/neg/t6666b.scala (limited to 'test/files/neg') diff --git a/test/files/neg/t6666b.check b/test/files/neg/t6666b.check new file mode 100644 index 0000000000..090bee800d --- /dev/null +++ b/test/files/neg/t6666b.check @@ -0,0 +1,4 @@ +t6666b.scala:6: error: Implementation restriction: access of object TreeOrd$1 from object TreeOrd$2, would require illegal premature access to the unconstructed `this` of class Test + implicit object TreeOrd extends Ordering[K](){ + ^ +one error found diff --git a/test/files/neg/t6666b.scala b/test/files/neg/t6666b.scala new file mode 100644 index 0000000000..0b8f1aa1a3 --- /dev/null +++ b/test/files/neg/t6666b.scala @@ -0,0 +1,17 @@ +import scala.collection.immutable.TreeMap +import scala.math.Ordering + +class Test[K](param:TreeMap[K,Int]){ + def this() = this({ + implicit object TreeOrd extends Ordering[K](){ + def compare(a: K, b: K) = { + -1 + } + } + new TreeMap[K, Int]() + }) +} + +object Test extends App { + new Test() +} -- cgit v1.2.3 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 +++------ test/files/neg/t6666.check | 18 ++---- test/files/neg/t6666.scala | 19 ------- test/files/neg/t6666b.check | 11 ++-- test/files/neg/t6666b.scala | 38 ++++++++----- test/files/neg/t6666c.check | 10 ++++ test/files/neg/t6666c.scala | 8 +++ test/files/neg/t6666d.check | 4 ++ test/files/neg/t6666d.scala | 18 ++++++ test/files/neg/t6666e.check | 4 ++ test/files/neg/t6666e.scala | 9 +++ 12 files changed, 146 insertions(+), 82 deletions(-) create mode 100644 test/files/neg/t6666c.check create mode 100644 test/files/neg/t6666c.scala create mode 100644 test/files/neg/t6666d.check create mode 100644 test/files/neg/t6666d.scala create mode 100644 test/files/neg/t6666e.check create mode 100644 test/files/neg/t6666e.scala (limited to 'test/files/neg') 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. */ diff --git a/test/files/neg/t6666.check b/test/files/neg/t6666.check index d0378173ea..63145f6ed7 100644 --- a/test/files/neg/t6666.check +++ b/test/files/neg/t6666.check @@ -16,25 +16,19 @@ t6666.scala:54: error: Implementation restriction: access of value x$7 in class t6666.scala:58: error: Implementation restriction: access of method x$8 in class C3 from anonymous class 9, would require illegal premature access to the unconstructed `this` of class C3 F.hof(() => x) ^ -t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$5, would require illegal premature access to the unconstructed `this` of class C4 +t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$3, would require illegal premature access to the unconstructed `this` of class C4 object Nested { def xx = x} ^ -t6666.scala:68: error: Implementation restriction: access of method x$10 in class C5 from object Nested$6, would require illegal premature access to the unconstructed `this` of class C5 - object Nested { def xx = x} - ^ -t6666.scala:83: error: Implementation restriction: access of method x$12 in class C11 from anonymous class 12, would require illegal premature access to the unconstructed `this` of class C11 +t6666.scala:76: error: Implementation restriction: access of method x$11 in class C11 from anonymous class 12, would require illegal premature access to the unconstructed `this` of class C11 F.byname(x) ^ -t6666.scala:102: error: Implementation restriction: access of method x$13 in class C13 from anonymous class 13, would require illegal premature access to the unconstructed `this` of class C13 +t6666.scala:95: error: Implementation restriction: access of method x$12 in class C13 from anonymous class 13, would require illegal premature access to the unconstructed `this` of class C13 F.hof(() => x) ^ -t6666.scala:111: error: Implementation restriction: access of method x$14 in class C14 from object Nested$7, would require illegal premature access to the unconstructed `this` of class C14 +t6666.scala:104: error: Implementation restriction: access of method x$13 in class C14 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C14 object Nested { def xx = x} ^ -t6666.scala:122: error: Implementation restriction: access of method x$15 in class C15 from object Nested$8, would require illegal premature access to the unconstructed `this` of class C15 - object Nested { def xx = x} - ^ -t6666.scala:131: error: Implementation restriction: access of method foo$1 in class COuter from class CInner$1, would require illegal premature access to the unconstructed `this` of class COuter +t6666.scala:112: error: Implementation restriction: access of method foo$1 in class COuter from class CInner$1, would require illegal premature access to the unconstructed `this` of class COuter class CInner extends C({foo}) ^ -13 errors found +11 errors found diff --git a/test/files/neg/t6666.scala b/test/files/neg/t6666.scala index d37ffaf141..19073884bd 100644 --- a/test/files/neg/t6666.scala +++ b/test/files/neg/t6666.scala @@ -62,13 +62,6 @@ class C4 extends C({ object Nested { def xx = x} Nested.xx }) -class C5 extends C({ - def x = "".toString - val y = { - object Nested { def xx = x} - Nested.xx - } -}) // okay, for same reason as O6 class C6 extends C({ @@ -114,18 +107,6 @@ class C14(a: Any) { } } -class C15(a: Any) { - def this() = { - this({ - def x = "".toString - val y = { - object Nested { def xx = x} - Nested.xx - } - }) - } -} - class COuter extends C({ def foo = 0 class CInner extends C({foo}) diff --git a/test/files/neg/t6666b.check b/test/files/neg/t6666b.check index 090bee800d..c3ffc7cfa9 100644 --- a/test/files/neg/t6666b.check +++ b/test/files/neg/t6666b.check @@ -1,4 +1,7 @@ -t6666b.scala:6: error: Implementation restriction: access of object TreeOrd$1 from object TreeOrd$2, would require illegal premature access to the unconstructed `this` of class Test - implicit object TreeOrd extends Ordering[K](){ - ^ -one error found +t6666b.scala:11: error: Implementation restriction: access of method x$1 in class C5 from object Nested$3, would require illegal premature access to the unconstructed `this` of class C5 + object Nested { def xx = x} + ^ +t6666b.scala:22: error: Implementation restriction: access of method x$2 in class C15 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C15 + object Nested { def xx = x} + ^ +two errors found diff --git a/test/files/neg/t6666b.scala b/test/files/neg/t6666b.scala index 0b8f1aa1a3..205ded76e5 100644 --- a/test/files/neg/t6666b.scala +++ b/test/files/neg/t6666b.scala @@ -1,17 +1,27 @@ -import scala.collection.immutable.TreeMap -import scala.math.Ordering - -class Test[K](param:TreeMap[K,Int]){ - def this() = this({ - implicit object TreeOrd extends Ordering[K](){ - def compare(a: K, b: K) = { - -1 - } - } - new TreeMap[K, Int]() - }) +class C(a: Any) +object F { + def byname(a: => Any) = println(a) + def hof(a: () => Any) = println(a()) } -object Test extends App { - new Test() + +class C5 extends C({ + def x = "".toString + val y = { + object Nested { def xx = x} + Nested.xx + } +}) + + +class C15(a: Any) { + def this() = { + this({ + def x = "".toString + val y = { + object Nested { def xx = x} + Nested.xx + } + }) + } } diff --git a/test/files/neg/t6666c.check b/test/files/neg/t6666c.check new file mode 100644 index 0000000000..8fb9f4ba14 --- /dev/null +++ b/test/files/neg/t6666c.check @@ -0,0 +1,10 @@ +t6666c.scala:2: error: Implementation restriction: access of method x$1 in class D from object X$4, would require illegal premature access to the unconstructed `this` of class D +class D extends C({def x = 0; object X { x }}) + ^ +t6666c.scala:5: error: Implementation restriction: access of method x$2 in class D1 from object X$5, would require illegal premature access to the unconstructed `this` of class D1 +class D1 extends C1({def x = 0; () => {object X { x }}}) + ^ +t6666c.scala:8: error: Implementation restriction: access of method x$3 from object X$6, would require illegal premature access to the unconstructed `this` of anonymous class 2 +class D2 extends C2({def x = 0; object X { x }}) + ^ +three errors found diff --git a/test/files/neg/t6666c.scala b/test/files/neg/t6666c.scala new file mode 100644 index 0000000000..76cc358307 --- /dev/null +++ b/test/files/neg/t6666c.scala @@ -0,0 +1,8 @@ +class C(a: Any) +class D extends C({def x = 0; object X { x }}) + +class C1(a: () => Any) +class D1 extends C1({def x = 0; () => {object X { x }}}) + +class C2(a: => Any) +class D2 extends C2({def x = 0; object X { x }}) diff --git a/test/files/neg/t6666d.check b/test/files/neg/t6666d.check new file mode 100644 index 0000000000..b4785f0129 --- /dev/null +++ b/test/files/neg/t6666d.check @@ -0,0 +1,4 @@ +t6666d.scala:7: error: Implementation restriction: access of object TreeOrd$1 from object TreeOrd$2, would require illegal premature access to the unconstructed `this` of class Test + implicit object TreeOrd extends Ordering[K](){ + ^ +one error found diff --git a/test/files/neg/t6666d.scala b/test/files/neg/t6666d.scala new file mode 100644 index 0000000000..49a688f91b --- /dev/null +++ b/test/files/neg/t6666d.scala @@ -0,0 +1,18 @@ + +import scala.collection.immutable.TreeMap +import scala.math.Ordering + +class Test[K](param:TreeMap[K,Int]){ + def this() = this({ + implicit object TreeOrd extends Ordering[K](){ + def compare(a: K, b: K) = { + -1 + } + } + new TreeMap[K, Int]() + }) +} + +object Test extends App { + new Test() +} diff --git a/test/files/neg/t6666e.check b/test/files/neg/t6666e.check new file mode 100644 index 0000000000..9fcc3ab718 --- /dev/null +++ b/test/files/neg/t6666e.check @@ -0,0 +1,4 @@ +t6666e.scala:8: error: Implementation restriction: anonymous class $anonfun requires premature access to class Crash. + this(Nil.collect{case x =>}) + ^ +one error found diff --git a/test/files/neg/t6666e.scala b/test/files/neg/t6666e.scala new file mode 100644 index 0000000000..120a5878b2 --- /dev/null +++ b/test/files/neg/t6666e.scala @@ -0,0 +1,9 @@ + +import scala.collection.immutable.TreeMap +import scala.math.Ordering + + +class Crash(a: Any) { + def this() = + this(Nil.collect{case x =>}) +} -- cgit v1.2.3 From 275b341545a3c4e633bd735cf45ccc1956a4233e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 29 Jan 2013 13:02:01 +0100 Subject: SI-6666 Catch VerifyErrors in the making in early defs. As we did for self/super calls, add a backstop into explicitouter and lambdalift to check when we try to get an outer pointer to an under-construction instance. --- src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala | 10 +++++----- test/files/neg/t6666.check | 9 ++++++--- test/files/neg/t6666.scala | 10 +++++++++- 3 files changed, 20 insertions(+), 9 deletions(-) (limited to 'test/files/neg') diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index a86d8aa2d6..0575254c26 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -269,10 +269,10 @@ abstract class ExplicitOuter extends InfoTransform } - /** The stack of constructor symbols in which a call to this() or to the super - * constructor is active. + /** The stack of class symbols in which a call to this() or to the super + * constructor, or early definition is active */ - protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls exists (_.owner == clazz) + protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls contains clazz protected val selfOrSuperCalls = mutable.Stack[Symbol]() @inline protected def inSelfOrSuperCall[A](sym: Symbol)(a: => A) = { selfOrSuperCalls push sym @@ -292,8 +292,8 @@ abstract class ExplicitOuter extends InfoTransform } case _ => } - if (treeInfo isSelfOrSuperConstrCall tree) // TODO also handle (and test) (treeInfo isEarlyDef tree) - inSelfOrSuperCall(currentOwner)(super.transform(tree)) + if ((treeInfo isSelfOrSuperConstrCall tree) || (treeInfo isEarlyDef tree)) + inSelfOrSuperCall(currentOwner.owner)(super.transform(tree)) else super.transform(tree) } diff --git a/test/files/neg/t6666.check b/test/files/neg/t6666.check index 63145f6ed7..6337d4c7d9 100644 --- a/test/files/neg/t6666.check +++ b/test/files/neg/t6666.check @@ -16,7 +16,7 @@ t6666.scala:54: error: Implementation restriction: access of value x$7 in class t6666.scala:58: error: Implementation restriction: access of method x$8 in class C3 from anonymous class 9, would require illegal premature access to the unconstructed `this` of class C3 F.hof(() => x) ^ -t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$3, would require illegal premature access to the unconstructed `this` of class C4 +t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C4 object Nested { def xx = x} ^ t6666.scala:76: error: Implementation restriction: access of method x$11 in class C11 from anonymous class 12, would require illegal premature access to the unconstructed `this` of class C11 @@ -25,10 +25,13 @@ t6666.scala:76: error: Implementation restriction: access of method x$11 in clas t6666.scala:95: error: Implementation restriction: access of method x$12 in class C13 from anonymous class 13, would require illegal premature access to the unconstructed `this` of class C13 F.hof(() => x) ^ -t6666.scala:104: error: Implementation restriction: access of method x$13 in class C14 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C14 +t6666.scala:104: error: Implementation restriction: access of method x$13 in class C14 from object Nested$5, would require illegal premature access to the unconstructed `this` of class C14 object Nested { def xx = x} ^ t6666.scala:112: error: Implementation restriction: access of method foo$1 in class COuter from class CInner$1, would require illegal premature access to the unconstructed `this` of class COuter class CInner extends C({foo}) ^ -11 errors found +t6666.scala:118: error: Implementation restriction: access of method x$14 in class CEarly from object Nested$6, would require illegal premature access to the unconstructed `this` of class CEarly + object Nested { def xx = x} + ^ +12 errors found diff --git a/test/files/neg/t6666.scala b/test/files/neg/t6666.scala index 19073884bd..1919ea3ca9 100644 --- a/test/files/neg/t6666.scala +++ b/test/files/neg/t6666.scala @@ -110,4 +110,12 @@ class C14(a: Any) { class COuter extends C({ def foo = 0 class CInner extends C({foo}) -}) \ No newline at end of file +}) + + +class CEarly(a: Any) extends { + val early = {def x = "".toString + object Nested { def xx = x} + Nested.xx + } +} with AnyRef \ No newline at end of file -- cgit v1.2.3