summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-01-24 00:35:19 +0100
committerJason Zaugg <jzaugg@gmail.com>2013-02-02 13:19:07 +0100
commit66fa1f22ac058e87350304388eca17aedc1e4b64 (patch)
treea478e541df1baf0748e43575021e18294d433247
parent4c34280e5bb3e48db35d97d890e4f5f1c5fb3a26 (diff)
downloadscala-66fa1f22ac058e87350304388eca17aedc1e4b64.tar.gz
scala-66fa1f22ac058e87350304388eca17aedc1e4b64.tar.bz2
scala-66fa1f22ac058e87350304388eca17aedc1e4b64.zip
Broader checks for poisonous this references.
Replaces more VerifyErrors with implementation restrictions.
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala64
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala25
-rw-r--r--test/files/neg/t6666.check18
-rw-r--r--test/files/neg/t6666.scala19
-rw-r--r--test/files/neg/t6666b.check11
-rw-r--r--test/files/neg/t6666b.scala38
-rw-r--r--test/files/neg/t6666c.check10
-rw-r--r--test/files/neg/t6666c.scala8
-rw-r--r--test/files/neg/t6666d.check4
-rw-r--r--test/files/neg/t6666d.scala18
-rw-r--r--test/files/neg/t6666e.check4
-rw-r--r--test/files/neg/t6666e.scala9
12 files changed, 146 insertions, 82 deletions
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 =>})
+}